diff --git a/CREDITS.txt b/CREDITS.txt
index c42ab7d..cb99950 100644
--- a/CREDITS.txt
+++ b/CREDITS.txt
@@ -7,6 +7,8 @@ org.xbib.time is based upon the following software:
- prettytime https://github.com/ocpsoft/prettytime (Apache 2.0)
+- cron expression https://github.com/anderswisch/cron-expression/ (MIT License)
+
with improvements by Jörg Prante including
- converted to Java 8 java.time API
@@ -16,3 +18,5 @@ with improvements by Jörg Prante including
- refactoring classes
- rewritten code to simplify and ease development
+
+- added nextExecution() method to cron expression and scheduling via Callable
diff --git a/build.gradle b/build.gradle
index 2b28c4a..3a96d10 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,76 +1,134 @@
plugins {
- id "org.sonarqube" version "2.2"
- id 'org.ajoberstar.github-pages' version '1.6.0-rc.1'
- id "org.xbib.gradle.plugin.jbake" version "1.2.1"
+ id "com.github.spotbugs" version "2.0.0"
+ id "io.codearte.nexus-staging" version "0.11.0"
}
-printf "Host: %s\nOS: %s %s %s\nJVM: %s %s %s %s\nGroovy: %s\nGradle: %s\n" +
- "Build: group: ${project.group} name: ${project.name} version: ${project.version}\n",
- InetAddress.getLocalHost(),
- System.getProperty("os.name"),
- System.getProperty("os.arch"),
- System.getProperty("os.version"),
- System.getProperty("java.version"),
- System.getProperty("java.vm.version"),
- System.getProperty("java.vm.vendor"),
- System.getProperty("java.vm.name"),
- GroovySystem.getVersion(),
- gradle.gradleVersion
-
apply plugin: 'java'
-apply plugin: 'maven'
-apply plugin: 'signing'
-apply plugin: 'findbugs'
apply plugin: 'pmd'
apply plugin: 'checkstyle'
-apply plugin: "jacoco"
-apply plugin: 'org.ajoberstar.github-pages'
-
-configurations {
- wagon
- provided
- testCompile.extendsFrom(provided)
-}
+apply plugin: "com.github.spotbugs"
dependencies {
- testCompile 'junit:junit:4.12'
- testCompile 'org.apache.logging.log4j:log4j-core:2.7'
- testCompile 'org.apache.logging.log4j:log4j-jul:2.7'
- wagon 'org.apache.maven.wagon:wagon-ssh-external:2.10'
+ testCompile "junit:junit:${project.property('junit.version')}"
+ testCompile "org.quartz-scheduler:quartz:${project.property('quartz.version')}"
+ testCompile "com.google.caliper:caliper:${project.property('caliper.version')}"
}
-sourceCompatibility = JavaVersion.VERSION_1_8
-targetCompatibility = JavaVersion.VERSION_1_8
+compileJava {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+}
+
+compileTestJava {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+}
-[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'
tasks.withType(JavaCompile) {
- options.compilerArgs << "-Xlint:all" << "-profile" << "compact2"
+ options.compilerArgs << "-Xlint:all"
}
test {
testLogging {
- showStandardStreams = false
+ showStandardStreams = true
exceptionFormat = 'full'
}
- systemProperty 'java.util.logging.manager', 'org.apache.logging.log4j.jul.LogManager'
+}
+
+spotbugs {
+ toolVersion = '3.1.12'
+ sourceSets = [sourceSets.main]
+ ignoreFailures = true
+ effort = "max"
+ reportLevel = "high"
+}
+
+tasks.withType(Pmd) {
+ ignoreFailures = true
+ reports {
+ xml.enabled = true
+ html.enabled = true
+ }
+}
+
+tasks.withType(Checkstyle) {
+ ignoreFailures = true
+ reports {
+ xml.enabled = true
+ html.enabled = true
+ }
}
task sourcesJar(type: Jar, dependsOn: classes) {
classifier 'sources'
from sourceSets.main.allSource
}
+
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier 'javadoc'
}
+
artifacts {
archives sourcesJar, javadocJar
}
-if (project.hasProperty('signing.keyId')) {
- signing {
- sign configurations.archives
+
+ext {
+ user = 'xbib'
+ projectName = 'time'
+ projectDescription = 'A bundle of Chronic, Prettytime, and org.joda.time.format optimized for Java 8 Time API'
+ scmUrl = 'https://github.com/xbib/time'
+ scmConnection = 'scm:git:git://github.com/xbib/time.git'
+ scmDeveloperConnection = 'scm:git:git://github.com/xbib/time.git'
+}
+
+task sonatypeUpload(type: Upload) {
+ configuration = configurations.archives
+ uploadDescriptor = true
+ repositories {
+ if (project.hasProperty('ossrhUsername')) {
+ mavenDeployer {
+ beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
+ repository(url: uri(ossrhReleaseUrl)) {
+ authentication(userName: ossrhUsername, password: ossrhPassword)
+ }
+ snapshotRepository(url: uri(ossrhSnapshotUrl)) {
+ authentication(userName: ossrhUsername, password: ossrhPassword)
+ }
+ pom.project {
+ name projectName
+ description projectDescription
+ packaging 'jar'
+ inceptionYear '2016'
+ url scmUrl
+ organization {
+ name 'xbib'
+ url 'http://xbib.org'
+ }
+ developers {
+ developer {
+ id user
+ name 'Jörg Prante'
+ email 'joergprante@gmail.com'
+ url 'https://github.com/jprante'
+ }
+ }
+ scm {
+ url scmUrl
+ connection scmConnection
+ developerConnection scmDeveloperConnection
+ }
+ licenses {
+ license {
+ name 'The Apache License, Version 2.0'
+ url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+ }
+ }
+ }
+ }
+ }
}
}
-apply from: 'gradle/ext.gradle'
-apply from: 'gradle/publish.gradle'
-apply from: 'gradle/sonarqube.gradle'
+nexusStaging {
+ packageGroup = "org.xbib"
+}
diff --git a/gradle.properties b/gradle.properties
index 852f762..ae672f0 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,3 +1,8 @@
group = org.xbib
name = time
-version = 1.0.0
+version = 2.0.0
+
+# test
+junit.version = 4.12
+quartz.version = 2.3.0
+caliper.version = 1.0-beta-2
diff --git a/gradle/ext.gradle b/gradle/ext.gradle
deleted file mode 100644
index 2146626..0000000
--- a/gradle/ext.gradle
+++ /dev/null
@@ -1,8 +0,0 @@
-ext {
- user = 'xbib'
- projectName = 'time'
- projectDescription = 'A bundle of Chronic, Prettytime, and org.joda.time.format optimized for Java 8 Time API'
- scmUrl = 'https://github.com/xbib/time'
- scmConnection = 'scm:git:git://github.com/xbib/time.git'
- scmDeveloperConnection = 'scm:git:git://github.com/xbib/time.git'
-}
diff --git a/gradle/publish.gradle b/gradle/publish.gradle
deleted file mode 100644
index 59f73fd..0000000
--- a/gradle/publish.gradle
+++ /dev/null
@@ -1,63 +0,0 @@
-
-task xbibUpload(type: Upload) {
- configuration = configurations.archives
- uploadDescriptor = true
- repositories {
- if (project.hasProperty('xbibUsername')) {
- mavenDeployer {
- configuration = configurations.wagon
- repository(url: uri('scpexe://xbib.org/repository')) {
- authentication(userName: xbibUsername, privateKey: xbibPrivateKey)
- }
- }
- }
- }
-}
-
-task sonatypeUpload(type: Upload) {
- configuration = configurations.archives
- uploadDescriptor = true
- repositories {
- if (project.hasProperty('ossrhUsername')) {
- mavenDeployer {
- beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
- repository(url: uri(ossrhReleaseUrl)) {
- authentication(userName: ossrhUsername, password: ossrhPassword)
- }
- snapshotRepository(url: uri(ossrhSnapshotUrl)) {
- authentication(userName: ossrhUsername, password: ossrhPassword)
- }
- pom.project {
- name projectName
- description projectDescription
- packaging 'jar'
- inceptionYear '2016'
- url scmUrl
- organization {
- name 'xbib'
- url 'http://xbib.org'
- }
- developers {
- developer {
- id user
- name 'Jörg Prante'
- email 'joergprante@gmail.com'
- url 'https://github.com/jprante'
- }
- }
- scm {
- url scmUrl
- connection scmConnection
- developerConnection scmDeveloperConnection
- }
- licenses {
- license {
- name 'The Apache License, Version 2.0'
- url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
- }
- }
- }
- }
- }
- }
-}
diff --git a/gradle/sonarqube.gradle b/gradle/sonarqube.gradle
deleted file mode 100644
index b31eafb..0000000
--- a/gradle/sonarqube.gradle
+++ /dev/null
@@ -1,36 +0,0 @@
-tasks.withType(FindBugs) {
- ignoreFailures = true
- reports {
- xml.enabled = true
- }
-}
-tasks.withType(Pmd) {
- ignoreFailures = true
- reports {
- xml.enabled = true
- }
-}
-tasks.withType(Checkstyle) {
- ignoreFailures = true
- reports {
- xml.enabled = true
- }
-}
-
-jacocoTestReport {
- reports {
- xml.enabled true
- xml.destination "${buildDir}/reports/jacoco-xml"
- }
-}
-
-sonarqube {
- properties {
- property "sonar.projectName", "${project.group} ${project.name}"
- property "sonar.sourceEncoding", "UTF-8"
- property "sonar.tests", "src/test/java"
- property "sonar.scm.provider", "git"
- property "sonar.java.coveragePlugin", "jacoco"
- property "sonar.junit.reportsPath", "build/test-results/test/"
- }
-}
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 51288f9..5c2d1cf 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 580ab8d..22f0587 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Nov 30 14:21:32 CET 2016
+#Mon Sep 09 15:32:28 CEST 2019
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6-all.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.2.1-all.zip
+zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
index 4453cce..83f2acf 100755
--- a/gradlew
+++ b/gradlew
@@ -1,5 +1,21 @@
#!/usr/bin/env sh
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed 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
+#
+# https://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.
+#
+
##############################################################################
##
## Gradle start up script for UN*X
@@ -28,16 +44,16 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
-warn ( ) {
+warn () {
echo "$*"
}
-die ( ) {
+die () {
echo
echo "$*"
echo
@@ -109,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
@@ -155,7 +171,7 @@ if $cygwin ; then
fi
# Escape application args
-save ( ) {
+save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
diff --git a/gradlew.bat b/gradlew.bat
index e95643d..24467a1 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,3 +1,19 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
diff --git a/src/main/java/org/xbib/time/chronic/Chronic.java b/src/main/java/org/xbib/time/chronic/Chronic.java
index 5711388..9ec7f74 100644
--- a/src/main/java/org/xbib/time/chronic/Chronic.java
+++ b/src/main/java/org/xbib/time/chronic/Chronic.java
@@ -33,39 +33,39 @@ public class Chronic {
/**
* Parses a string containing a natural language date or time. If the parser
* can find a date or time, either a Time or Chronic::Span will be returned
- * (depending on the value of :guess ). If no date or time can be found,
+ * (depending on the value of {@code :guess}). If no date or time can be found,
* +nil+ will be returned.
*
* Options are:
*
- * [:context ]
- * :past or :future (defaults to :future )
+ * [{@code :context}]
+ * {@code :past} or {@code :future} (defaults to {@code :future})
*
- * If your string represents a birthday, you can set :context to :past
+ * If your string represents a birthday, you can set {@code :context} to {@code :past}
* and if an ambiguous string is given, it will assume it is in the
- * past. Specify :future or omit to set a future context.
+ * past. Specify {@code :future<} or omit to set a future context.
*
- * [:now ]
+ * [{@code :now}]
* Time (defaults to Time.now)
*
- * By setting :now to a Time, all computations will be based off
+ * By setting {@code :now} to a Time, all computations will be based off
* of that time instead of Time.now
*
- * [:guess ]
+ * [{@code :guess<}]
* +true+ or +false+ (defaults to +true+)
*
* By default, the parser will guess a single point in time for the
* given date or time. If you'd rather have the entire time span returned,
- * set :guess to +false+ and a Chronic::Span will be returned.
+ * set {@code :guess} to +false+ and a Chronic::Span will be returned.
*
- * [:ambiguous_time_range ]
- * Integer or :none (defaults to 6 (6am-6pm))
+ * [{@code :ambiguous_time_range}]
+ * Integer or {@code :none} (defaults to {@code 6} (6am-6pm))
*
* If an Integer is given, ambiguous times (like 5:00) will be
* assumed to be within the range of that time in the AM to that time
- * in the PM. For example, if you set it to 7 , then the parser will
+ * in the PM. For example, if you set it to {@code 7}, then the parser will
* look for the time between 7am and 7pm. In the case of 5:00, it would
- * assume that means 5:00pm. If :none is given, no assumption
+ * assume that means 5:00pm. If {@code :none} is given, no assumption
* will be made, and the first matching instance of that time will
* be used.
* @param text text
diff --git a/src/main/java/org/xbib/time/chronic/repeaters/RepeaterUnit.java b/src/main/java/org/xbib/time/chronic/repeaters/RepeaterUnit.java
index dd70bee..d758ce5 100644
--- a/src/main/java/org/xbib/time/chronic/repeaters/RepeaterUnit.java
+++ b/src/main/java/org/xbib/time/chronic/repeaters/RepeaterUnit.java
@@ -43,7 +43,9 @@ public abstract class RepeaterUnit extends Repeater {
String unitName = unitNameEnum.name();
String capitalizedUnitName = unitName.substring(0, 1) + unitName.substring(1).toLowerCase();
String repeaterClassName = RepeaterUnit.class.getPackage().getName() + ".Repeater" + capitalizedUnitName;
- return Class.forName(repeaterClassName).asSubclass(RepeaterUnit.class).newInstance();
+ return Class.forName(repeaterClassName)
+ .asSubclass(RepeaterUnit.class)
+ .getConstructor().newInstance();
}
}
return null;
diff --git a/src/main/java/org/xbib/time/schedule/CronExpression.java b/src/main/java/org/xbib/time/schedule/CronExpression.java
new file mode 100644
index 0000000..4ae3d89
--- /dev/null
+++ b/src/main/java/org/xbib/time/schedule/CronExpression.java
@@ -0,0 +1,130 @@
+package org.xbib.time.schedule;
+
+import java.time.ZonedDateTime;
+import java.util.Map;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public abstract class CronExpression {
+
+ public abstract boolean matches(ZonedDateTime t);
+
+ public abstract ZonedDateTime nextExecution(ZonedDateTime from, ZonedDateTime to);
+
+ private static final String YEARLY = "0 0 1 1 *",
+ MONTHLY = "0 0 1 * *",
+ WEEKLY = "0 0 * * 7",
+ DAILY = "0 0 * * *",
+ HOURLY = "0 * * * *";
+
+ private static final Map ALIASES = Map.ofEntries(
+ Map.entry("yearly", YEARLY),
+ Map.entry("annually", YEARLY),
+ Map.entry("monthly", MONTHLY),
+ Map.entry("weekly", WEEKLY),
+ Map.entry("daily", DAILY),
+ Map.entry("midnight", DAILY),
+ Map.entry("hourly", HOURLY));
+
+ private static final Pattern ALIAS_PATTERN = Pattern.compile("[a-z]+");
+
+ private static final boolean DEFAULT_ONE_BASED_DAY_OF_WEEK = false;
+
+ private static final boolean DEFAULT_SECONDS = false;
+
+ private static final boolean DEFAULT_ALLOW_BOTH_DAYS = true;
+
+ public static CronExpression yearly() {
+ return parse(YEARLY);
+ }
+
+ public static CronExpression monthly() {
+ return parse(MONTHLY);
+ }
+
+ public static CronExpression weekly() {
+ return parse(WEEKLY);
+ }
+
+ public static CronExpression daily() {
+ return parse(DAILY);
+ }
+
+ public static CronExpression hourly() {
+ return parse(HOURLY);
+ }
+
+ public static boolean isValid(String s) {
+ return isValid(s, DEFAULT_ONE_BASED_DAY_OF_WEEK, DEFAULT_SECONDS, DEFAULT_ALLOW_BOTH_DAYS);
+ }
+
+ public static CronExpression parse(String s) {
+ return parse(s, DEFAULT_ONE_BASED_DAY_OF_WEEK, DEFAULT_SECONDS, DEFAULT_ALLOW_BOTH_DAYS);
+ }
+
+ private static boolean isValid(String s, boolean oneBasedDayOfWeek, boolean seconds, boolean allowBothDays) {
+ boolean valid;
+ try {
+ parse(s, oneBasedDayOfWeek, seconds, allowBothDays);
+ valid = true;
+ } catch (Exception e) {
+ valid = false;
+ }
+ return valid;
+ }
+
+ private static CronExpression parse(String s, boolean oneBasedDayOfWeek, boolean seconds, boolean allowBothDays) {
+ Objects.requireNonNull(s);
+ if (s.charAt(0) == '@') {
+ Matcher aliasMatcher = ALIAS_PATTERN.matcher(s);
+ if (aliasMatcher.find(1)) {
+ String alias = aliasMatcher.group();
+ if (ALIASES.containsKey(alias)) {
+ return new DefaultCronExpression(ALIASES.get(alias),
+ DEFAULT_ONE_BASED_DAY_OF_WEEK, DEFAULT_SECONDS, DEFAULT_ALLOW_BOTH_DAYS);
+ } else if ("reboot".equals(alias)) {
+ return new RebootCronExpression();
+ }
+ }
+ }
+ return new DefaultCronExpression(s, seconds, oneBasedDayOfWeek, allowBothDays);
+ }
+
+ public static Parser parser() {
+ return new Parser();
+ }
+
+ public static class Parser {
+ private boolean oneBasedDayOfWeek, seconds, allowBothDays;
+
+ private Parser() {
+ oneBasedDayOfWeek = DEFAULT_ONE_BASED_DAY_OF_WEEK;
+ seconds = DEFAULT_SECONDS;
+ allowBothDays = DEFAULT_ALLOW_BOTH_DAYS;
+ }
+
+ public boolean isValid(String s) {
+ return CronExpression.isValid(s, oneBasedDayOfWeek, seconds, allowBothDays);
+ }
+
+ public CronExpression parse(String s) {
+ return CronExpression.parse(s, oneBasedDayOfWeek, seconds, allowBothDays);
+ }
+
+ public Parser withOneBasedDayOfWeek(boolean oneBasedDayOfWeek) {
+ this.oneBasedDayOfWeek = oneBasedDayOfWeek;
+ return this;
+ }
+
+ public Parser withSecondsField(boolean secondsField) {
+ this.seconds = secondsField;
+ return this;
+ }
+
+ public Parser allowBothDayFields(boolean allowBothDayFields) {
+ this.allowBothDays = allowBothDayFields;
+ return this;
+ }
+ }
+}
diff --git a/src/main/java/org/xbib/time/schedule/CronSchedule.java b/src/main/java/org/xbib/time/schedule/CronSchedule.java
new file mode 100644
index 0000000..7c7d2bf
--- /dev/null
+++ b/src/main/java/org/xbib/time/schedule/CronSchedule.java
@@ -0,0 +1,82 @@
+package org.xbib.time.schedule;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.time.Clock;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+public class CronSchedule implements Closeable {
+
+ private final ScheduledExecutorService executor;
+
+ private final List> entries;
+
+ private final int periodInMilliseconds;
+
+ private ScheduledFuture> future;
+
+ public CronSchedule(ScheduledExecutorService scheduledExecutorServices) {
+ this(scheduledExecutorServices, 60000);
+ }
+
+ public CronSchedule(ScheduledExecutorService scheduledExecutorServices,
+ int periodInMilliseconds) {
+ this.executor = scheduledExecutorServices;
+ this.entries = new ArrayList<>();
+ this.periodInMilliseconds = periodInMilliseconds;
+ }
+
+ public void add(String name, CronExpression expression, Callable callable) {
+ entries.add(new Entry(name, expression, callable));
+ }
+
+ public void remove(String name) {
+ entries.removeIf(entry -> name.equals(entry.getName()));
+ }
+
+ public void start() {
+ long initialDelay = periodInMilliseconds - (Clock.systemDefaultZone().millis() % periodInMilliseconds);
+ this.future = executor.scheduleAtFixedRate(CronSchedule.this::run,
+ initialDelay, periodInMilliseconds, TimeUnit.MILLISECONDS);
+ }
+
+ public void run() {
+ run(ZonedDateTime.now());
+ }
+
+ public void run(ZonedDateTime time) {
+ for (Entry entry : entries) {
+ if (entry.getCronExpression().matches(time)) {
+ entry.setLastCalled(time);
+ executor.submit(entry.getCallable());
+ }
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (future != null) {
+ future.cancel(true);
+ future = null;
+ }
+ if (executor != null) {
+ executor.shutdownNow();
+ try {
+ executor.awaitTermination(30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new IOException(e);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return entries.toString();
+ }
+}
diff --git a/src/main/java/org/xbib/time/schedule/DayOfMonthField.java b/src/main/java/org/xbib/time/schedule/DayOfMonthField.java
new file mode 100644
index 0000000..a0f1c25
--- /dev/null
+++ b/src/main/java/org/xbib/time/schedule/DayOfMonthField.java
@@ -0,0 +1,122 @@
+package org.xbib.time.schedule;
+
+import java.time.DayOfWeek;
+import java.time.ZonedDateTime;
+import java.time.temporal.TemporalAdjusters;
+
+public class DayOfMonthField extends DefaultField {
+
+ private final boolean lastDay;
+
+ private final boolean nearestWeekday;
+
+ private final boolean unspecified;
+
+ private DayOfMonthField(Builder b) {
+ super(b);
+ this.lastDay = b.lastDay;
+ this.nearestWeekday = b.nearestWeekday;
+ this.unspecified = b.unspecified;
+ }
+
+ boolean isUnspecified() {
+ return unspecified;
+ }
+
+ public boolean matches(ZonedDateTime time) {
+ if (unspecified) {
+ return true;
+ }
+ final int dayOfMonth = time.getDayOfMonth();
+ if (lastDay) {
+ return dayOfMonth == time.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth();
+ } else if (nearestWeekday) {
+ DayOfWeek dayOfWeek = time.getDayOfWeek();
+ if ((dayOfWeek == DayOfWeek.MONDAY && contains(time.minusDays(1).getDayOfMonth()))
+ || (dayOfWeek == DayOfWeek.FRIDAY && contains(time.plusDays(1).getDayOfMonth()))) {
+ return true;
+ }
+ }
+ return contains(dayOfMonth);
+ }
+
+ public static DayOfMonthField parse(Tokens s) {
+ return new Builder().parse(s).build();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+ DayOfMonthField that = (DayOfMonthField) o;
+ return lastDay == that.lastDay && nearestWeekday == that.nearestWeekday;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + (lastDay ? 1 : 0);
+ result = 31 * result + (nearestWeekday ? 1 : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return isFullRange() ? "*" : getNumbers().toString();
+ }
+
+ public static class Builder extends DefaultField.Builder {
+
+ private boolean lastDay;
+
+ private boolean nearestWeekday;
+
+ private boolean unspecified;
+
+ Builder() {
+ super(1, 31);
+ }
+
+ @Override
+ public DayOfMonthField build() {
+ return new DayOfMonthField(this);
+ }
+
+ @Override
+ protected Builder parse(Tokens tokens) {
+ super.parse(tokens);
+ return this;
+ }
+
+ @Override
+ protected boolean parseValue(Tokens tokens, Token token, int first, int last) {
+ if (token == Token.MATCH_ONE) {
+ unspecified = true;
+ return false;
+ } else if (token == Token.LAST) {
+ lastDay = true;
+ return false;
+ } else {
+ return super.parseValue(tokens, token, first, last);
+ }
+ }
+
+ @Override
+ protected boolean parseNumber(Tokens tokens, Token token, int first, int last) {
+ if (token == Token.WEEKDAY) {
+ add(first);
+ nearestWeekday = true;
+ return false;
+ } else {
+ return super.parseNumber(tokens, token, first, last);
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/xbib/time/schedule/DayOfWeekField.java b/src/main/java/org/xbib/time/schedule/DayOfWeekField.java
new file mode 100644
index 0000000..4451868
--- /dev/null
+++ b/src/main/java/org/xbib/time/schedule/DayOfWeekField.java
@@ -0,0 +1,173 @@
+package org.xbib.time.schedule;
+
+import org.xbib.time.util.LinkedHashSetMultiMap;
+import org.xbib.time.util.MultiMap;
+import java.time.DayOfWeek;
+import java.time.ZonedDateTime;
+import java.time.temporal.TemporalAdjusters;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class DayOfWeekField extends DefaultField {
+
+ private final MultiMap nth;
+
+ private final Set last;
+
+ private final boolean hasNth;
+
+ private final boolean hasLast;
+
+ private final boolean unspecified;
+
+ private DayOfWeekField(Builder b) {
+ super(b);
+ this.nth = b.nth;
+ hasNth = !nth.isEmpty();
+ this.last = b.last;
+ hasLast = !last.isEmpty();
+ unspecified = b.unspecified;
+ }
+
+ public boolean isUnspecified() {
+ return unspecified;
+ }
+
+ public boolean matches(ZonedDateTime time) {
+ if (unspecified) {
+ return true;
+ }
+ final DayOfWeek dayOfWeek = time.getDayOfWeek();
+ //int number = dayOfWeek.getValue() % 7;
+ int number = dayOfWeek.getValue();
+ if (hasLast) {
+ return last.contains(number) && time.getMonth() != time.plusWeeks(1).getMonth();
+ } else if (hasNth) {
+ int dayOfYear = time.getDayOfYear();
+ if (nth.containsKey(number)) {
+ for (int possibleMatch : nth.get(number)) {
+ if (dayOfYear == time.with(TemporalAdjusters.dayOfWeekInMonth(possibleMatch, dayOfWeek)).getDayOfYear()) {
+ return true;
+ }
+ }
+ }
+ }
+ return contains(number);
+ }
+
+ private int number(int dayOfWeek) {
+ return dayOfWeek % 7;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+ DayOfWeekField that = (DayOfWeekField) o;
+ if (hasLast != that.hasLast) {
+ return false;
+ }
+ if (hasNth != that.hasNth) {
+ return false;
+ }
+ if (!last.equals(that.last)) {
+ return false;
+ }
+ return nth.equals(that.nth);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + nth.hashCode();
+ result = 31 * result + last.hashCode();
+ result = 31 * result + (hasNth ? 1 : 0);
+ result = 31 * result + (hasLast ? 1 : 0);
+ return result;
+ }
+
+ public static DayOfWeekField parse(Tokens s, boolean oneBased) {
+ return new Builder(oneBased).parse(s).build();
+ }
+
+ public static class Builder extends DefaultField.Builder {
+ static final Keywords KEYWORDS = new Keywords();
+
+ static {
+ KEYWORDS.put("MON", 1);
+ KEYWORDS.put("TUE", 2);
+ KEYWORDS.put("WED", 3);
+ KEYWORDS.put("THU", 4);
+ KEYWORDS.put("FRI", 5);
+ KEYWORDS.put("SAT", 6);
+ KEYWORDS.put("SUN", 7);
+ }
+
+ private boolean oneBased;
+
+ private boolean unspecified;
+
+ private final Set last;
+
+ private final MultiMap nth;
+
+ Builder(boolean oneBased) {
+ super(1, 7);
+ this.oneBased = oneBased;
+ last = new LinkedHashSet<>();
+ nth = new LinkedHashSetMultiMap<>();
+ }
+
+ @Override
+ protected Builder parse(Tokens tokens) {
+ tokens.keywords(KEYWORDS);
+ if (oneBased) {
+ tokens.offset(1);
+ }
+ super.parse(tokens);
+ tokens.reset();
+ return this;
+ }
+
+ @Override
+ protected boolean parseValue(Tokens tokens, Token token, int first, int last) {
+ if (token == Token.MATCH_ONE) {
+ unspecified = true;
+ return false;
+ } else {
+ return super.parseValue(tokens, token, first, last);
+ }
+ }
+
+ @Override
+ protected boolean parseNumber(Tokens tokens, Token token, int first, int last) {
+ if (token == Token.LAST) {
+ this.last.add(first);
+ } else if (token == Token.NTH) {
+ int number = nextNumber(tokens);
+ if (oneBased) {
+ number += 1;
+ }
+ if (number == 0) {
+ number = 7;
+ }
+ nth.put(first, number);
+ } else {
+ return super.parseNumber(tokens, token, first, last);
+ }
+ return false;
+ }
+
+ @Override
+ public DayOfWeekField build() {
+ return new DayOfWeekField(this);
+ }
+ }
+}
diff --git a/src/main/java/org/xbib/time/schedule/DefaultCronExpression.java b/src/main/java/org/xbib/time/schedule/DefaultCronExpression.java
new file mode 100644
index 0000000..11bc125
--- /dev/null
+++ b/src/main/java/org/xbib/time/schedule/DefaultCronExpression.java
@@ -0,0 +1,228 @@
+package org.xbib.time.schedule;
+
+import java.time.DayOfWeek;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalAdjusters;
+import java.util.Objects;
+import java.util.SortedSet;
+import java.util.logging.Logger;
+
+public class DefaultCronExpression extends CronExpression {
+
+ private final String string;
+
+ private final TimeField second;
+
+ private final TimeField minute;
+
+ private final TimeField hour;
+
+ private final TimeField month;
+
+ private final TimeField year;
+
+ private final DayOfWeekField dayOfWeek;
+
+ private final DayOfMonthField dayOfMonth;
+
+ DefaultCronExpression(String string, boolean seconds, boolean oneBasedDayOfWeek, boolean allowBothDayFields) {
+ this.string = string;
+ if (string.isEmpty()) {
+ throw new IllegalArgumentException("empty spec not allowed");
+ }
+ String s = string.toUpperCase();
+ Tokens tokens = new Tokens(s);
+ if (seconds) {
+ second = DefaultField.parse(tokens, 0, 59);
+ } else {
+ second = MatchAllField.instance;
+ }
+ minute = DefaultField.parse(tokens, 0, 59);
+ hour = DefaultField.parse(tokens, 0, 23);
+ dayOfMonth = DayOfMonthField.parse(tokens);
+ month = MonthField.parse(tokens);
+ dayOfWeek = DayOfWeekField.parse(tokens, oneBasedDayOfWeek);
+ if (tokens.hasNext()) {
+ year = DefaultField.parse(tokens, 0, 0);
+ } else {
+ year = MatchAllField.instance;
+ }
+ if (!allowBothDayFields && !dayOfMonth.isUnspecified() && !dayOfWeek.isUnspecified()) {
+ throw new IllegalArgumentException("Day of month and day of week may not both be specified");
+ }
+ }
+
+ @Override
+ public boolean matches(ZonedDateTime t) {
+ return second.contains(t.getSecond()) &&
+ minute.contains(t.getMinute()) &&
+ hour.contains(t.getHour()) &&
+ month.contains(t.getMonthValue()) &&
+ year.contains(t.getYear()) &&
+ dayOfWeek.matches(t) &&
+ dayOfMonth.matches(t);
+ }
+
+ @Override
+ public ZonedDateTime nextExecution(ZonedDateTime from,
+ ZonedDateTime to) {
+ ZonedDateTime next = second instanceof MatchAllField ?
+ from.plusMinutes(1).truncatedTo(ChronoUnit.MINUTES) : from.plusSeconds(1).truncatedTo(ChronoUnit.SECONDS);
+ while (true) {
+ if (next.isBefore(from) || next.isAfter(to)) {
+ throw new IllegalStateException("out of range: " + from + " < " + next + " < " + to + " -> " + this);
+ }
+ SortedSet set;
+ if (!year.contains(next.getYear())) {
+ if (!year.isFullRange()) {
+ set = year.getNumbers().tailSet(next.getYear());
+ if (set.isEmpty()) {
+ next = next.plusYears(1);
+ continue;
+ } else {
+ next = next.plusYears(set.first() - next.getYear());
+ }
+ }
+ }
+ if (!month.contains(next.getMonthValue())) {
+ if (!month.isFullRange()) {
+ set = month.getNumbers().tailSet(next.getMonthValue());
+ if (set.isEmpty()) {
+ next = next.plusMonths(1);
+ continue;
+ } else {
+ next = next.plusMonths(set.first() - next.getMonthValue());
+ }
+ }
+ }
+ if (!dayOfMonth.isUnspecified()) {
+ if (!dayOfMonth.contains(next.getDayOfMonth())) {
+ if (!dayOfMonth.isFullRange()) {
+ set = dayOfMonth.getNumbers().tailSet(next.getDayOfMonth());
+ if (set.isEmpty()) {
+ next = next.plusDays(1).truncatedTo(ChronoUnit.DAYS);
+ continue;
+ } else {
+ next = next.plusDays(set.first() - next.getDayOfMonth())
+ .truncatedTo(ChronoUnit.DAYS);
+ }
+ }
+ }
+ }
+ if (!dayOfWeek.isUnspecified()) {
+ if (!dayOfWeek.contains(next.getDayOfWeek().getValue())) {
+ if (!dayOfWeek.isFullRange()) {
+ set = dayOfWeek.getNumbers().tailSet(next.getDayOfWeek().getValue());
+ if (set.isEmpty()) {
+ next = next.plusDays(1).truncatedTo(ChronoUnit.DAYS);
+ continue;
+ } else {
+ DayOfWeek dayOfWeek = DayOfWeek.of(set.first());
+ next = next.with(TemporalAdjusters.next(dayOfWeek));
+
+ }
+ }
+ }
+ }
+ if (!hour.contains(next.getHour())) {
+ if (!hour.isFullRange()) {
+ set = hour.getNumbers().tailSet(next.getHour());
+ if (set.isEmpty()) {
+ next = next.plusHours(1).truncatedTo(ChronoUnit.HOURS);
+ continue;
+ } else {
+ next = next.plusHours(set.first() - next.getHour())
+ .truncatedTo(ChronoUnit.HOURS);
+ }
+ }
+ }
+ if (!minute.contains(next.getMinute())) {
+ if (!minute.isFullRange()) {
+ set = minute.getNumbers().tailSet(next.getMinute());
+ if (set.isEmpty()) {
+ next = next.plusMinutes(1).truncatedTo(ChronoUnit.MINUTES);
+ continue;
+ } else {
+ next = next.plusMinutes(set.first() - next.getMinute())
+ .truncatedTo(ChronoUnit.MINUTES);
+ }
+ }
+ }
+ if (!(second instanceof MatchAllField)) {
+ if (!second.contains(next.getSecond())) {
+ if (!second.isFullRange()) {
+ set = second.getNumbers().tailSet(next.getSecond());
+ if (set.isEmpty()) {
+ next = next.plusSeconds(1).truncatedTo(ChronoUnit.SECONDS);
+ continue;
+ } else {
+ next = next.plusSeconds(set.first() - next.getSecond())
+ .truncatedTo(ChronoUnit.SECONDS);
+ }
+ }
+ }
+ }
+ break;
+ }
+ return next;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ DefaultCronExpression that = (DefaultCronExpression) o;
+ if (!Objects.equals(dayOfMonth, that.dayOfMonth)) {
+ return false;
+ }
+ if (!Objects.equals(dayOfWeek, that.dayOfWeek)) {
+ return false;
+ }
+ if (!Objects.equals(hour, that.hour)) {
+ return false;
+ }
+ if (!Objects.equals(minute, that.minute)) {
+ return false;
+ }
+ if (!Objects.equals(month, that.month)) {
+ return false;
+ }
+ if (!Objects.equals(second, that.second)) {
+ return false;
+ }
+ if (!string.equals(that.string)) {
+ return false;
+ }
+ return Objects.equals(year, that.year);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = string.hashCode();
+ result = 31 * result + (second != null ? second.hashCode() : 0);
+ result = 31 * result + (minute != null ? minute.hashCode() : 0);
+ result = 31 * result + (hour != null ? hour.hashCode() : 0);
+ result = 31 * result + (month != null ? month.hashCode() : 0);
+ result = 31 * result + (year != null ? year.hashCode() : 0);
+ result = 31 * result + (dayOfWeek != null ? dayOfWeek.hashCode() : 0);
+ result = 31 * result + (dayOfMonth != null ? dayOfMonth.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "CronExpression[" + string + " -> seconds=" + second +
+ ",mins=" + minute +
+ ",hrs=" + hour +
+ ",dayOfMonths=" + dayOfMonth +
+ ",dayOfWeek=" + dayOfWeek +
+ ",months=" + month +
+ ",yrs=" + year +
+ "]";
+ }
+}
diff --git a/src/main/java/org/xbib/time/schedule/DefaultField.java b/src/main/java/org/xbib/time/schedule/DefaultField.java
new file mode 100644
index 0000000..c7a1715
--- /dev/null
+++ b/src/main/java/org/xbib/time/schedule/DefaultField.java
@@ -0,0 +1,177 @@
+package org.xbib.time.schedule;
+
+import java.util.NavigableSet;
+import java.util.Objects;
+import java.util.TreeSet;
+
+public class DefaultField implements TimeField {
+
+ private final boolean fullRange;
+
+ private final NavigableSet numbers;
+
+ DefaultField(Builder b) {
+ fullRange = b.fullRange;
+ numbers = fullRange ? null : b.numbers;
+ }
+
+ public static DefaultField parse(Tokens s, int min, int max) {
+ return new Builder(min, max).parse(s).build();
+ }
+
+ @Override
+ public boolean contains(int number) {
+ return fullRange || numbers.contains(number);
+ }
+
+ @Override
+ public NavigableSet getNumbers() {
+ return numbers;
+ }
+
+ @Override
+ public boolean isFullRange() {
+ return fullRange;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ DefaultField that = (DefaultField) o;
+ if (fullRange != that.fullRange) {
+ return false;
+ }
+ return Objects.equals(numbers, that.numbers);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (fullRange ? 1 : 0);
+ result = 31 * result + (numbers != null ? numbers.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return isFullRange() ? "*" : getNumbers().toString();
+ }
+
+ public static class Builder {
+
+ private final NavigableSet numbers;
+
+ private final int min;
+
+ private final int max;
+
+ private boolean fullRange;
+
+ Builder(int min, int max) {
+ this.min = min;
+ this.max = max;
+ numbers = new TreeSet<>();
+ }
+
+ protected Builder parse(Tokens tokens) {
+ Token token;
+ while (!endOfField(token = tokens.next())) {
+ if (parseValue(tokens, token, min, max)) {
+ break;
+ }
+ }
+ return this;
+ }
+
+ protected boolean parseValue(Tokens tokens, Token token, int first, int last) {
+ if (token == Token.NUMBER) {
+ return parseNumber(tokens, tokens.next(), tokens.number(), last);
+ } else if (token == Token.MATCH_ALL) {
+ token = tokens.next();
+ if (token == Token.SKIP) {
+ rangeSkip(first, last, nextNumber(tokens));
+ } else if (token == Token.VALUE_SEPARATOR) {
+ range(first, last);
+ } else if (endOfField(token)) {
+ range(first, last);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the end of this field has been reached.
+ * @param tokens tokens
+ * @param token token
+ * @param first first
+ * @param last last
+ * @return true if end reached
+ */
+ protected boolean parseNumber(Tokens tokens, Token token, int first, int last) {
+ Token t = token;
+ int l = last;
+ if (t == Token.SKIP) {
+ rangeSkip(first, l, nextNumber(tokens));
+ } else if (t == Token.RANGE) {
+ l = nextNumber(tokens);
+ t = tokens.next();
+ if (t == Token.SKIP) {
+ rangeSkip(first, l, nextNumber(tokens));
+ } else if (t == Token.VALUE_SEPARATOR) {
+ range(first, l);
+ } else if (endOfField(t)) {
+ range(first, l);
+ return true;
+ }
+ } else if (t == Token.VALUE_SEPARATOR) {
+ add(first);
+ } else if (endOfField(t)) {
+ add(first);
+ return true;
+ }
+ return false;
+ }
+
+ int nextNumber(Tokens tokens) {
+ if (tokens.next() == Token.NUMBER) {
+ return tokens.number();
+ }
+ throw new IllegalStateException("Expected number");
+ }
+
+ private boolean endOfField(Token token) {
+ return token == Token.FIELD_SEPARATOR || token == Token.END_OF_INPUT;
+ }
+
+ void rangeSkip(int first, int last, int skip) {
+ for (int i = first; i <= last; i++) {
+ if ((i - min) % skip == 0) {
+ add(i);
+ }
+ }
+ }
+
+ protected void range(int first, int last) {
+ if (first == min && last == max) {
+ fullRange = true;
+ } else {
+ for (int i = first; i <= last; i++) {
+ add(i);
+ }
+ }
+ }
+
+ protected void add(int value) {
+ numbers.add(value);
+ }
+
+ public DefaultField build() {
+ return new DefaultField(this);
+ }
+ }
+}
diff --git a/src/main/java/org/xbib/time/schedule/Entry.java b/src/main/java/org/xbib/time/schedule/Entry.java
new file mode 100644
index 0000000..776498d
--- /dev/null
+++ b/src/main/java/org/xbib/time/schedule/Entry.java
@@ -0,0 +1,57 @@
+package org.xbib.time.schedule;
+
+import java.time.ZonedDateTime;
+import java.util.concurrent.Callable;
+
+class Entry {
+
+ private String name;
+
+ private CronExpression cronExpression;
+
+ private Callable callable;
+
+ private ZonedDateTime lastCalled;
+
+ private ZonedDateTime nextCall;
+
+ Entry(String name, CronExpression cronExpression, Callable callable) {
+ this.name = name;
+ this.cronExpression = cronExpression;
+ this.callable = callable;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public CronExpression getCronExpression() {
+ return cronExpression;
+ }
+
+ public Callable getCallable() {
+ return callable;
+ }
+
+ public void setLastCalled(ZonedDateTime lastCalled) {
+ this.lastCalled = lastCalled;
+ // heuristic, limit to 1 year ahead
+ this.nextCall = cronExpression.nextExecution(lastCalled, lastCalled.plusYears(1));
+ }
+
+ public ZonedDateTime getLastCalled() {
+ return lastCalled;
+ }
+
+ public ZonedDateTime getNextCall() {
+ return nextCall;
+ }
+
+ @Override
+ public String toString() {
+ return "Entry[name=" + name + ", expression=" + cronExpression +
+ ",callable= " + callable +
+ ",lastcalled=" + lastCalled +
+ ",nextcall=" + nextCall + "]";
+ }
+}
diff --git a/src/main/java/org/xbib/time/schedule/Keywords.java b/src/main/java/org/xbib/time/schedule/Keywords.java
new file mode 100644
index 0000000..ed6fa29
--- /dev/null
+++ b/src/main/java/org/xbib/time/schedule/Keywords.java
@@ -0,0 +1,42 @@
+package org.xbib.time.schedule;
+
+import java.util.Arrays;
+
+final class Keywords {
+ private final int[][][] keywords = new int[26][26][26];
+
+ public Keywords() {
+ for (int[][] second : keywords) {
+ for (int[] third : second) {
+ Arrays.fill(third, -1);
+ }
+ }
+ }
+
+ public void put(String keyword, int value) {
+ keywords[letterAt(keyword, 0)][letterAt(keyword, 1)][letterAt(keyword, 2)] = value;
+ }
+
+ public int get(String s, int start, int end) {
+ if (end - start != 3) {
+ throw new IllegalArgumentException();
+ }
+ int number = keywords[arrayIndex(s, start)][arrayIndex(s, start + 1)][arrayIndex(s, start + 2)];
+ if (number >= 0) {
+ return number;
+ }
+ throw new IllegalArgumentException();
+ }
+
+ private int arrayIndex(String s, int charIndex) {
+ int index = letterAt(s, charIndex);
+ if (index < 0 || index >= keywords.length) {
+ throw new IllegalArgumentException();
+ }
+ return index;
+ }
+
+ private static int letterAt(String s, int charIndex) {
+ return s.charAt(charIndex) - 'A';
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/xbib/time/schedule/MatchAllField.java b/src/main/java/org/xbib/time/schedule/MatchAllField.java
new file mode 100644
index 0000000..09d3831
--- /dev/null
+++ b/src/main/java/org/xbib/time/schedule/MatchAllField.java
@@ -0,0 +1,23 @@
+package org.xbib.time.schedule;
+
+import java.util.NavigableSet;
+
+public enum MatchAllField implements TimeField {
+
+ instance;
+
+ @Override
+ public boolean contains(int number) {
+ return true;
+ }
+
+ @Override
+ public NavigableSet getNumbers() {
+ return null;
+ }
+
+ @Override
+ public boolean isFullRange() {
+ return true;
+ }
+}
diff --git a/src/main/java/org/xbib/time/schedule/MonthField.java b/src/main/java/org/xbib/time/schedule/MonthField.java
new file mode 100644
index 0000000..8dc91a8
--- /dev/null
+++ b/src/main/java/org/xbib/time/schedule/MonthField.java
@@ -0,0 +1,48 @@
+package org.xbib.time.schedule;
+
+public class MonthField extends DefaultField {
+
+ private MonthField(Builder b) {
+ super(b);
+ }
+
+ public static MonthField parse(Tokens s) {
+ return new Builder().parse(s).build();
+ }
+
+ public static class Builder extends DefaultField.Builder {
+ protected static final Keywords KEYWORDS = new Keywords();
+
+ static {
+ KEYWORDS.put("JAN", 1);
+ KEYWORDS.put("FEB", 2);
+ KEYWORDS.put("MAR", 3);
+ KEYWORDS.put("APR", 4);
+ KEYWORDS.put("MAY", 5);
+ KEYWORDS.put("JUN", 6);
+ KEYWORDS.put("JUL", 7);
+ KEYWORDS.put("AUG", 8);
+ KEYWORDS.put("SEP", 9);
+ KEYWORDS.put("OCT", 10);
+ KEYWORDS.put("NOV", 11);
+ KEYWORDS.put("DEC", 12);
+ }
+
+ Builder() {
+ super(1, 12);
+ }
+
+ @Override
+ protected Builder parse(Tokens tokens) {
+ tokens.keywords(KEYWORDS);
+ super.parse(tokens);
+ tokens.reset();
+ return this;
+ }
+
+ @Override
+ public MonthField build() {
+ return new MonthField(this);
+ }
+ }
+}
diff --git a/src/main/java/org/xbib/time/schedule/RebootCronExpression.java b/src/main/java/org/xbib/time/schedule/RebootCronExpression.java
new file mode 100644
index 0000000..311d854
--- /dev/null
+++ b/src/main/java/org/xbib/time/schedule/RebootCronExpression.java
@@ -0,0 +1,26 @@
+package org.xbib.time.schedule;
+
+import java.time.ZonedDateTime;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Matches once only.
+ */
+final class RebootCronExpression extends CronExpression {
+
+ private final AtomicBoolean matchOnce;
+
+ RebootCronExpression() {
+ matchOnce = new AtomicBoolean(true);
+ }
+
+ @Override
+ public boolean matches(ZonedDateTime t) {
+ return matchOnce.getAndSet(false);
+ }
+
+ @Override
+ public ZonedDateTime nextExecution(ZonedDateTime from, ZonedDateTime to) {
+ return null;
+ }
+}
diff --git a/src/main/java/org/xbib/time/schedule/TimeField.java b/src/main/java/org/xbib/time/schedule/TimeField.java
new file mode 100644
index 0000000..2eae374
--- /dev/null
+++ b/src/main/java/org/xbib/time/schedule/TimeField.java
@@ -0,0 +1,12 @@
+package org.xbib.time.schedule;
+
+import java.util.NavigableSet;
+
+public interface TimeField {
+
+ boolean contains(int number);
+
+ NavigableSet getNumbers();
+
+ boolean isFullRange();
+}
diff --git a/src/main/java/org/xbib/time/schedule/Token.java b/src/main/java/org/xbib/time/schedule/Token.java
new file mode 100644
index 0000000..c5eb584
--- /dev/null
+++ b/src/main/java/org/xbib/time/schedule/Token.java
@@ -0,0 +1,15 @@
+package org.xbib.time.schedule;
+
+enum Token {
+ END_OF_INPUT,
+ FIELD_SEPARATOR,
+ LAST,
+ MATCH_ALL,
+ MATCH_ONE,
+ NTH,
+ NUMBER,
+ RANGE,
+ SKIP,
+ VALUE_SEPARATOR,
+ WEEKDAY
+}
diff --git a/src/main/java/org/xbib/time/schedule/Tokens.java b/src/main/java/org/xbib/time/schedule/Tokens.java
new file mode 100644
index 0000000..ad950f0
--- /dev/null
+++ b/src/main/java/org/xbib/time/schedule/Tokens.java
@@ -0,0 +1,189 @@
+package org.xbib.time.schedule;
+
+final class Tokens {
+
+ private int number;
+
+ private int offset;
+
+ private Keywords keywords;
+
+ private final String source;
+ private final int length;
+ private int position;
+
+ public Tokens(String s) {
+ source = s;
+ length = s.length();
+ position = 0;
+ }
+
+ public int number() {
+ return number;
+ }
+
+ public void offset(int offset) {
+ this.offset = offset;
+ }
+
+ public void keywords(Keywords k) {
+ keywords = k;
+ }
+
+ public void reset() {
+ offset = 0;
+ keywords = null;
+ }
+
+ public boolean hasNext() {
+ return hasNextChar();
+ }
+
+ public Token next() {
+ if (position >= length) {
+ return Token.END_OF_INPUT;
+ }
+ int start = position;
+ char c = currentChar();
+ switch (c) {
+ case ' ':
+ case '\t':
+ do {
+ if (!hasNextChar()) {
+ position++;
+ break;
+ }
+ c = nextChar();
+ } while (isWhitespace(c));
+ return Token.FIELD_SEPARATOR;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ do {
+ if (!hasNextChar()) {
+ position++;
+ break;
+ }
+ c = nextChar();
+ } while (isDigit(c));
+ number = Integer.parseInt(substringFrom(start)) - offset;
+ return Token.NUMBER;
+ case ',':
+ position++;
+ return Token.VALUE_SEPARATOR;
+ case '*':
+ position++;
+ return Token.MATCH_ALL;
+ case '-':
+ position++;
+ return Token.RANGE;
+ case '/':
+ position++;
+ return Token.SKIP;
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ do {
+ if (!hasNextChar()) {
+ position++;
+ break;
+ }
+ c = nextChar();
+ } while (isLetter(c));
+ if (position - start == 1) {
+ c = source.charAt(start);
+ if (c == 'L') {
+ return Token.LAST;
+ } else if (c == 'W') {
+ return Token.WEEKDAY;
+ }
+ throw new IllegalArgumentException(badCharacter(c, start));
+ } else {
+ if (keywords != null) {
+ try {
+ int mapped = keywords.get(source, start, position);
+ if (mapped != -1) {
+ number = mapped;
+ return Token.NUMBER;
+ }
+ } catch (IllegalArgumentException ignore) {
+ }
+ }
+ throw new IllegalArgumentException(badKeyword(start));
+ }
+ case '?':
+ position++;
+ return Token.MATCH_ONE;
+ case '#':
+ position++;
+ return Token.NTH;
+ }
+ throw new IllegalArgumentException(badCharacter(c, position));
+ }
+
+ private String badCharacter(char c, int index) {
+ return "Bad character '" + c + "' at position " + index + " in string: " + source;
+ }
+
+ private String badKeyword(int start) {
+ return "Bad keyword '" + substringFrom(start) + "' at position " + start + " in string: " + source;
+ }
+
+ private String substringFrom(int start) {
+ return source.substring(start, position);
+ }
+
+ private boolean hasNextChar() {
+ return position < length - 1;
+ }
+
+ private char nextChar() {
+ return source.charAt(++position);
+ }
+
+ private char currentChar() {
+ return source.charAt(position);
+ }
+
+ private static boolean isLetter(char c) {
+ return 'A' <= c && c <= 'Z';
+ }
+
+ private static boolean isDigit(char c) {
+ return '0' <= c && c <= '9';
+ }
+
+ private static boolean isWhitespace(char c) {
+ return c == ' ' || c == '\t';
+ }
+}
diff --git a/src/main/java/org/xbib/time/schedule/package-info.java b/src/main/java/org/xbib/time/schedule/package-info.java
new file mode 100644
index 0000000..15b052e
--- /dev/null
+++ b/src/main/java/org/xbib/time/schedule/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Schedule jobs (like cron).
+ */
+package org.xbib.time.schedule;
diff --git a/src/main/java/org/xbib/time/util/AbstractMultiMap.java b/src/main/java/org/xbib/time/util/AbstractMultiMap.java
new file mode 100644
index 0000000..41a2b30
--- /dev/null
+++ b/src/main/java/org/xbib/time/util/AbstractMultiMap.java
@@ -0,0 +1,126 @@
+package org.xbib.time.util;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Abstract multi map.
+ *
+ * @param the key type parameter
+ * @param the value type parameter
+ */
+abstract class AbstractMultiMap implements MultiMap {
+
+ private final Map> map;
+
+ AbstractMultiMap() {
+ this(null);
+ }
+
+ private AbstractMultiMap(MultiMap map) {
+ this.map = newMap();
+ if (map != null) {
+ putAll(map);
+ }
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public void clear() {
+ map.clear();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ @Override
+ public boolean containsKey(K key) {
+ return map.containsKey(key);
+ }
+
+ @Override
+ public Set keySet() {
+ return map.keySet();
+ }
+
+ @Override
+ public boolean put(K key, V value) {
+ Collection set = map.get(key);
+ if (set == null) {
+ set = newValues();
+ set.add(value);
+ map.put(key, set);
+ return true;
+ } else {
+ set.add(value);
+ return false;
+ }
+ }
+
+ @Override
+ public void putAll(K key, Iterable values) {
+ if (values == null) {
+ return;
+ }
+ Collection set = map.computeIfAbsent(key, k -> newValues());
+ for (V v : values) {
+ set.add(v);
+ }
+ }
+
+ @Override
+ public Collection get(K key) {
+ return map.get(key);
+ }
+
+ @Override
+ public Collection remove(K key) {
+ return map.remove(key);
+ }
+
+ @Override
+ public boolean remove(K key, V value) {
+ Collection set = map.get(key);
+ return set != null && set.remove(value);
+ }
+
+ @Override
+ public void putAll(MultiMap map) {
+ if (map != null) {
+ for (K key : map.keySet()) {
+ putAll(key, map.get(key));
+ }
+ }
+ }
+
+ @Override
+ public Map> asMap() {
+ return map;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof AbstractMultiMap && map.equals(((AbstractMultiMap) obj).map);
+ }
+
+ @Override
+ public int hashCode() {
+ return map.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return map.toString();
+ }
+
+ abstract Collection newValues();
+
+ abstract Map> newMap();
+}
diff --git a/src/main/java/org/xbib/time/util/LinkedHashSetMultiMap.java b/src/main/java/org/xbib/time/util/LinkedHashSetMultiMap.java
new file mode 100644
index 0000000..80fad1a
--- /dev/null
+++ b/src/main/java/org/xbib/time/util/LinkedHashSetMultiMap.java
@@ -0,0 +1,29 @@
+package org.xbib.time.util;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+
+/**
+ * Linked multi map.
+ *
+ * @param the key type parameter
+ * @param the value type parameter
+ */
+public class LinkedHashSetMultiMap extends AbstractMultiMap {
+
+ public LinkedHashSetMultiMap() {
+ super();
+ }
+
+ @Override
+ Collection newValues() {
+ return new LinkedHashSet<>();
+ }
+
+ @Override
+ Map> newMap() {
+ return new LinkedHashMap<>();
+ }
+}
diff --git a/src/main/java/org/xbib/time/util/MultiMap.java b/src/main/java/org/xbib/time/util/MultiMap.java
new file mode 100644
index 0000000..9f1df0e
--- /dev/null
+++ b/src/main/java/org/xbib/time/util/MultiMap.java
@@ -0,0 +1,38 @@
+package org.xbib.time.util;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * MultiMap interface.
+ *
+ * @param the key type parameter
+ * @param the value type parameter
+ */
+public interface MultiMap {
+
+ void clear();
+
+ int size();
+
+ boolean isEmpty();
+
+ boolean containsKey(K key);
+
+ Collection get(K key);
+
+ Set keySet();
+
+ boolean put(K key, V value);
+
+ void putAll(K key, Iterable values);
+
+ void putAll(MultiMap map);
+
+ Collection remove(K key);
+
+ boolean remove(K key, V value);
+
+ Map> asMap();
+}
diff --git a/src/main/java/org/xbib/time/util/TreeMultiMap.java b/src/main/java/org/xbib/time/util/TreeMultiMap.java
new file mode 100644
index 0000000..89ef157
--- /dev/null
+++ b/src/main/java/org/xbib/time/util/TreeMultiMap.java
@@ -0,0 +1,31 @@
+package org.xbib.time.util;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * A {@link TreeMap} based multi map. The keys ore ordered by a comparator.
+ * @param te key type
+ * @param the value type
+ */
+public class TreeMultiMap extends AbstractMultiMap {
+
+ private final Comparator comparator;
+
+ public TreeMultiMap(Comparator comparator) {
+ this.comparator = comparator;
+ }
+
+ @Override
+ Map> newMap() {
+ return new TreeMap<>(comparator);
+ }
+
+ @Override
+ Collection newValues() {
+ return new LinkedHashSet<>();
+ }
+}
diff --git a/src/main/java/org/xbib/time/util/package-info.java b/src/main/java/org/xbib/time/util/package-info.java
new file mode 100644
index 0000000..5163410
--- /dev/null
+++ b/src/main/java/org/xbib/time/util/package-info.java
@@ -0,0 +1,4 @@
+/**
+ *
+ */
+package org.xbib.time.util;
diff --git a/src/test/java/org/xbib/time/FormatterTest.java b/src/test/java/org/xbib/time/DateTimeFormatterTest.java
similarity index 95%
rename from src/test/java/org/xbib/time/FormatterTest.java
rename to src/test/java/org/xbib/time/DateTimeFormatterTest.java
index 83f15d8..fd67d18 100644
--- a/src/test/java/org/xbib/time/FormatterTest.java
+++ b/src/test/java/org/xbib/time/DateTimeFormatterTest.java
@@ -10,7 +10,7 @@ import java.util.Locale;
import static org.junit.Assert.assertEquals;
-public class FormatterTest {
+public class DateTimeFormatterTest {
@Test
public void testLocalDate() {
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeAPIManipulationTest.java b/src/test/java/org/xbib/time/pretty/PrettyTimeAPIManipulationTest.java
index b61d578..4164d38 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeAPIManipulationTest.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeAPIManipulationTest.java
@@ -10,8 +10,10 @@ import static org.junit.Assert.assertNotNull;
public class PrettyTimeAPIManipulationTest {
- List list = null;
- PrettyTime t = new PrettyTime();
+ private List list = null;
+
+ private PrettyTime t = new PrettyTime();
+
private TimeUnitQuantity timeUnitQuantity = null;
@Test
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_AR_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_AR_Test.java
index 7f2675c..8159155 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_AR_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_AR_Test.java
@@ -1,250 +1,252 @@
-package org.xbib.time.pretty;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.time.temporal.ChronoField;
-import java.util.List;
-import java.util.Locale;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-public class PrettyTimeI18n_AR_Test {
-
- private Locale locale;
-
- @Before
- public void setUp() throws Exception {
- locale = new Locale("ar");
- Locale.setDefault(locale);
- }
-
- @Test
- public void testCeilingInterval() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.of(2009, 6, 17, 0, 0);
- PrettyTime p = new PrettyTime(localDateTime);
- assertEquals("1 شهر مضت", p.format(LocalDateTime.of(2009, 5, 20, 0, 0)));
- }
-
- @Test
- public void testRightNow() throws Exception {
- PrettyTime t = new PrettyTime();
- assertEquals("بعد لحظات", t.format(LocalDateTime.now()));
- }
-
- @Test
- public void testRightNowVariance() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("بعد لحظات", t.format(600));
- }
-
- @Test
- public void testMinutesFromNow() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("12 دقائق من الآن", t.format(1000 * 60 * 12));
- }
-
- @Test
- public void testHoursFromNow() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("3 ساعات من الآن", t.format(1000 * 60 * 60 * 3));
- }
-
- @Test
- public void testDaysFromNow() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("3 ايام من الآن", t.format(1000 * 60 * 60 * 24 * 3));
- }
-
- @Test
- public void testWeeksFromNow() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("3 أسابيع من الآن", t.format(1000 * 60 * 60 * 24 * 7 * 3));
- }
-
- @Test
- public void testMonthsFromNow() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("3 أشهر من الآن", t.format(2629743830L * 3L));
- }
-
- @Test
- public void testYearsFromNow() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("3 سنوات من الآن", t.format(2629743830L * 12L * 3L));
- }
-
- @Test
- public void testDecadesFromNow() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("3 عقود من الآن", t.format(315569259747L * 3L));
- }
-
- @Test
- public void testCenturiesFromNow() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("3 قرون من الآن", t.format(3155692597470L * 3L));
- }
-
- @Test
- public void testMomentsAgo() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(6000), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("منذ لحظات", t.format(0));
- }
-
- @Test
- public void testMinutesAgo() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 12), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("12 دقائق مضت", t.format(0));
- }
-
- @Test
- public void testHoursAgo() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 3), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("3 ساعات مضت", t.format(0));
- }
-
- @Test
- public void testDaysAgo() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 3), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("3 ايام مضت", t.format(0));
- }
-
- @Test
- public void testWeeksAgo() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 7 * 3), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("3 أسابيع مضت", t.format(0));
- }
-
- @Test
- public void testMonthsAgo() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(2629743830L * 3L), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("3 أشهر مضت", t.format(0));
- }
-
- @Test
- public void testCustomFormat() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- TimeUnit unit = new TimeUnit() {
- @Override
- public long getMaxQuantity() {
- return 0;
- }
-
- @Override
- public long getMillisPerUnit() {
- return 5000;
- }
- };
- t.clearUnits();
- t.registerUnit(unit, new SimpleTimeFormat()
- .setSingularName("tick").setPluralName("ticks")
- .setPattern("%n %u").setRoundingTolerance(20)
- .setFutureSuffix("... RUN!")
- .setFuturePrefix("self destruct in: ").setPastPrefix("self destruct was: ").setPastSuffix(
- " ago..."));
-
- assertEquals("self destruct in: 5 ticks ... RUN!", t.format(25000));
- localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(25000), ZoneId.systemDefault());
- t.setReference(localDateTime);
- assertEquals("self destruct was: 5 ticks ago...", t.format(0));
- }
-
- @Test
- public void testYearsAgo() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(2629743830L * 12L * 3L), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("3 سنوات مضت", t.format(0));
- }
-
- @Test
- public void testDecadesAgo() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(315569259747L * 3L), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("3 عقود مضت", t.format(0));
- }
-
- @Test
- public void testCenturiesAgo() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(3155692597470L * 3L), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("3 قرون مضت", t.format(0));
- }
-
- @Test
- public void testWithinTwoHoursRounding() throws Exception {
- PrettyTime t = new PrettyTime();
- LocalDateTime localDateTime = LocalDateTime.now().minus(6543990, ChronoField.MILLI_OF_SECOND.getBaseUnit());
- assertEquals("2 ساعات مضت", t.format(localDateTime));
- }
-
- @Test
- public void testPreciseInTheFuture() throws Exception {
- PrettyTime t = new PrettyTime();
- LocalDateTime localDateTime = LocalDateTime.now().plusSeconds(10 * 60 + 5 * 60 * 60);
- List timeUnitQuantities = t.calculatePreciseDuration(localDateTime);
- assertTrue(timeUnitQuantities.size() >= 2);
- assertEquals(5, timeUnitQuantities.get(0).getQuantity());
- assertEquals(10, timeUnitQuantities.get(1).getQuantity());
- }
-
- @Test
- public void testPreciseInThePast() throws Exception {
- PrettyTime t = new PrettyTime();
- LocalDateTime localDateTime = LocalDateTime.now().minusSeconds(10 * 60 + 5 * 60 * 60);
- List timeUnitQuantities = t.calculatePreciseDuration(localDateTime);
- assertTrue(timeUnitQuantities.size() >= 2);
- assertEquals(-5, timeUnitQuantities.get(0).getQuantity());
- assertTrue(timeUnitQuantities.get(1).getQuantity() == -9 || timeUnitQuantities.get(1).getQuantity() == -10);
- }
-
- @Test
- public void testFormattingDurationListInThePast() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 3 + 1000 * 60 * 60 * 15 + 1000 * 60 * 38), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- List timeUnitQuantities = t.calculatePreciseDuration(LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault()));
- assertEquals("3 ايام 15 ساعات 38 دقائق مضت", t.format(timeUnitQuantities));
- }
-
- @Test
- public void testFormattingDurationListInTheFuture() throws Exception {
- PrettyTime t = new PrettyTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault()));
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 3 + 1000 * 60 * 60 * 15 + 1000 * 60 * 38), ZoneId.systemDefault());
- List timeUnitQuantities = t.calculatePreciseDuration(localDateTime);
- assertEquals("3 ايام 15 ساعات 38 دقائق من الآن", t.format(timeUnitQuantities));
- }
-
- @Test
- public void testSetLocale() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(315569259747L * 3L), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("3 عقود مضت", t.format(0));
- }
-
- @After
- public void tearDown() throws Exception {
- Locale.setDefault(locale);
- }
-
-}
+package org.xbib.time.pretty;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.temporal.ChronoField;
+import java.util.List;
+import java.util.Locale;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class PrettyTimeI18n_AR_Test {
+
+ private Locale defaultLocale;
+
+ private Locale locale;
+
+ @Before
+ public void setUp() throws Exception {
+ defaultLocale = Locale.getDefault();
+ locale = new Locale("ar");
+ Locale.setDefault(locale);
+ }
+
+ @Test
+ public void testCeilingInterval() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.of(2009, 6, 17, 0, 0);
+ PrettyTime p = new PrettyTime(localDateTime);
+ assertEquals("1 شهر مضت", p.format(LocalDateTime.of(2009, 5, 20, 0, 0)));
+ }
+
+ @Test
+ public void testRightNow() throws Exception {
+ PrettyTime t = new PrettyTime();
+ assertEquals("بعد لحظات", t.format(LocalDateTime.now()));
+ }
+
+ @Test
+ public void testRightNowVariance() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("بعد لحظات", t.format(600));
+ }
+
+ @Test
+ public void testMinutesFromNow() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("12 دقائق من الآن", t.format(1000 * 60 * 12));
+ }
+
+ @Test
+ public void testHoursFromNow() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("3 ساعات من الآن", t.format(1000 * 60 * 60 * 3));
+ }
+
+ @Test
+ public void testDaysFromNow() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("3 ايام من الآن", t.format(1000 * 60 * 60 * 24 * 3));
+ }
+
+ @Test
+ public void testWeeksFromNow() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("3 أسابيع من الآن", t.format(1000 * 60 * 60 * 24 * 7 * 3));
+ }
+
+ @Test
+ public void testMonthsFromNow() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("3 أشهر من الآن", t.format(2629743830L * 3L));
+ }
+
+ @Test
+ public void testYearsFromNow() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("3 سنوات من الآن", t.format(2629743830L * 12L * 3L));
+ }
+
+ @Test
+ public void testDecadesFromNow() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("3 عقود من الآن", t.format(315569259747L * 3L));
+ }
+
+ @Test
+ public void testCenturiesFromNow() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("3 قرون من الآن", t.format(3155692597470L * 3L));
+ }
+
+ @Test
+ public void testMomentsAgo() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(6000), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("منذ لحظات", t.format(0));
+ }
+
+ @Test
+ public void testMinutesAgo() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 12), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("12 دقائق مضت", t.format(0));
+ }
+
+ @Test
+ public void testHoursAgo() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 3), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("3 ساعات مضت", t.format(0));
+ }
+
+ @Test
+ public void testDaysAgo() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 3), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("3 ايام مضت", t.format(0));
+ }
+
+ @Test
+ public void testWeeksAgo() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 7 * 3), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("3 أسابيع مضت", t.format(0));
+ }
+
+ @Test
+ public void testMonthsAgo() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(2629743830L * 3L), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("3 أشهر مضت", t.format(0));
+ }
+
+ @Test
+ public void testCustomFormat() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ TimeUnit unit = new TimeUnit() {
+ @Override
+ public long getMaxQuantity() {
+ return 0;
+ }
+
+ @Override
+ public long getMillisPerUnit() {
+ return 5000;
+ }
+ };
+ t.clearUnits();
+ t.registerUnit(unit, new SimpleTimeFormat()
+ .setSingularName("tick").setPluralName("ticks")
+ .setPattern("%n %u").setRoundingTolerance(20)
+ .setFutureSuffix("... RUN!")
+ .setFuturePrefix("self destruct in: ").setPastPrefix("self destruct was: ").setPastSuffix(
+ " ago..."));
+
+ assertEquals("self destruct in: 5 ticks ... RUN!", t.format(25000));
+ localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(25000), ZoneId.systemDefault());
+ t.setReference(localDateTime);
+ assertEquals("self destruct was: 5 ticks ago...", t.format(0));
+ }
+
+ @Test
+ public void testYearsAgo() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(2629743830L * 12L * 3L), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("3 سنوات مضت", t.format(0));
+ }
+
+ @Test
+ public void testDecadesAgo() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(315569259747L * 3L), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("3 عقود مضت", t.format(0));
+ }
+
+ @Test
+ public void testCenturiesAgo() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(3155692597470L * 3L), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("3 قرون مضت", t.format(0));
+ }
+
+ @Test
+ public void testWithinTwoHoursRounding() throws Exception {
+ PrettyTime t = new PrettyTime();
+ LocalDateTime localDateTime = LocalDateTime.now().minus(6543990, ChronoField.MILLI_OF_SECOND.getBaseUnit());
+ assertEquals("2 ساعات مضت", t.format(localDateTime));
+ }
+
+ @Test
+ public void testPreciseInTheFuture() throws Exception {
+ PrettyTime t = new PrettyTime();
+ LocalDateTime localDateTime = LocalDateTime.now().plusSeconds(10 * 60 + 5 * 60 * 60);
+ List timeUnitQuantities = t.calculatePreciseDuration(localDateTime);
+ assertTrue(timeUnitQuantities.size() >= 2);
+ assertEquals(5, timeUnitQuantities.get(0).getQuantity());
+ assertEquals(10, timeUnitQuantities.get(1).getQuantity());
+ }
+
+ @Test
+ public void testPreciseInThePast() throws Exception {
+ PrettyTime t = new PrettyTime();
+ LocalDateTime localDateTime = LocalDateTime.now().minusSeconds(10 * 60 + 5 * 60 * 60);
+ List timeUnitQuantities = t.calculatePreciseDuration(localDateTime);
+ assertTrue(timeUnitQuantities.size() >= 2);
+ assertEquals(-5, timeUnitQuantities.get(0).getQuantity());
+ assertTrue(timeUnitQuantities.get(1).getQuantity() == -9 || timeUnitQuantities.get(1).getQuantity() == -10);
+ }
+
+ @Test
+ public void testFormattingDurationListInThePast() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 3 + 1000 * 60 * 60 * 15 + 1000 * 60 * 38), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ List timeUnitQuantities = t.calculatePreciseDuration(LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault()));
+ assertEquals("3 ايام 15 ساعات 38 دقائق مضت", t.format(timeUnitQuantities));
+ }
+
+ @Test
+ public void testFormattingDurationListInTheFuture() throws Exception {
+ PrettyTime t = new PrettyTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault()));
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 3 + 1000 * 60 * 60 * 15 + 1000 * 60 * 38), ZoneId.systemDefault());
+ List timeUnitQuantities = t.calculatePreciseDuration(localDateTime);
+ assertEquals("3 ايام 15 ساعات 38 دقائق من الآن", t.format(timeUnitQuantities));
+ }
+
+ @Test
+ public void testSetLocale() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(315569259747L * 3L), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("3 عقود مضت", t.format(0));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ Locale.setDefault(defaultLocale);
+ }
+}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_BG_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_BG_Test.java
index 0ddcc86..dd9cd16 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_BG_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_BG_Test.java
@@ -1,224 +1,224 @@
-package org.xbib.time.pretty;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.util.List;
-import java.util.Locale;
-
-import static org.junit.Assert.assertEquals;
-
-public class PrettyTimeI18n_BG_Test {
-
- // Stores current locale so that it can be restored
- private Locale locale;
-
- // Method setUp() is called automatically before every test method
- @Before
- public void setUp() throws Exception {
- locale = Locale.getDefault();
- Locale.setDefault(new Locale("bg"));
- }
-
- @Test
- public void testCenturiesFromNow() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0L), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("след 3 века", t.format(3155692597470L * 3L));
- }
-
- @Test
- public void testCenturiesAgo() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(3155692597470L * 3L), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("преди 3 века", t.format(0));
- }
-
- @Test
- public void testCenturySingular() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(3155692597470L), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("преди 1 век", t.format(0));
- }
-
- @Test
- public void testDaysFromNow() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("след 3 дни", t.format(1000 * 60 * 60 * 24 * 3));
- }
-
- @Test
- public void testDaysAgo() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 3), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("преди 3 дни", t.format(0));
- }
-
- @Test
- public void testDaySingular() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("преди 1 ден", t.format(0));
- }
-
- @Test
- public void testDecadesAgo() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(315569259747L * 3L), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("преди 3 десетилетия", t.format(0));
- }
-
- @Test
- public void testDecadesFromNow() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("след 3 десетилетия", t.format(315569259747L * 3L));
- }
-
- @Test
- public void testDecadeSingular() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("след 1 десетилетие", t.format(315569259747L));
- }
-
- @Test
- public void testHoursFromNow() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("след 3 часа", t.format(1000 * 60 * 60 * 3));
- }
-
- @Test
- public void testHoursAgo() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 3), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("преди 3 часа", t.format(0));
- }
-
- @Test
- public void testHourSingular() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("преди 1 час", t.format(0));
- }
-
- @Test
- public void testRightNow() throws Exception {
- PrettyTime t = new PrettyTime();
- assertEquals("в момента", t.format(LocalDateTime.now()));
- }
-
- @Test
- public void testMomentsAgo() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(6000), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("току що", t.format(0));
- }
-
- @Test
- public void testMinutesFromNow() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("след 12 минути", t.format(1000 * 60 * 12));
- }
-
- @Test
- public void testMinutesAgo() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 12), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("преди 12 минути", t.format(0));
- }
-
- @Test
- public void testMonthsFromNow() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("след 3 месеца", t.format((2629743830L * 3L)));
- }
-
- @Test
- public void testMonthsAgo() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(2629743830L * 3L), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("преди 3 месеца", t.format(0));
- }
-
- @Test
- public void testMonthSingular() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(2629743830L), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("преди 1 месец", t.format(0));
- }
-
- @Test
- public void testWeeksFromNow() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("след 3 седмици", t.format(1000 * 60 * 60 * 24 * 7 * 3));
- }
-
- @Test
- public void testWeeksAgo() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 7 * 3), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("преди 3 седмици", t.format(0));
- }
-
- @Test
- public void testWeekSingular() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 7), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("преди 1 седмица", t.format(0));
- }
-
- @Test
- public void testYearsFromNow() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("след 3 години", t.format(2629743830L * 12L * 3L));
- }
-
- @Test
- public void testYearsAgo() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(2629743830L * 12L * 3L), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("преди 3 години", t.format(0));
- }
-
- @Test
- public void testYearSingular() throws Exception {
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(2629743830L * 12L), ZoneId.systemDefault());
- PrettyTime t = new PrettyTime(localDateTime);
- assertEquals("преди 1 година", t.format(0));
- }
-
- @Test
- public void testFormattingDurationListInThePast() throws Exception {
- PrettyTime t = new PrettyTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 3 + 1000 * 60 * 60 * 15 + 1000 * 60 * 38), ZoneId.systemDefault()));
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0L), ZoneId.systemDefault());
- List timeUnitQuantities = t.calculatePreciseDuration(localDateTime);
- assertEquals("преди 3 дни 15 часа 38 минути", t.format(timeUnitQuantities));
- }
-
- @Test
- public void testFormattingDurationListInTheFuture() throws Exception {
- PrettyTime t = new PrettyTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(0L), ZoneId.systemDefault()));
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 3 + 1000 * 60 * 60 * 15
- + 1000 * 60 * 38), ZoneId.systemDefault());
- List timeUnitQuantities = t.calculatePreciseDuration(localDateTime);
- assertEquals("след 3 дни 15 часа 38 минути", t.format(timeUnitQuantities));
- }
-
- // Method tearDown() is called automatically after every test method
- @After
- public void tearDown() throws Exception {
- Locale.setDefault(locale);
- }
-
-}
+package org.xbib.time.pretty;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.List;
+import java.util.Locale;
+
+import static org.junit.Assert.assertEquals;
+
+public class PrettyTimeI18n_BG_Test {
+
+ private Locale defaultLocale;
+
+ private Locale locale;
+
+ @Before
+ public void setUp() throws Exception {
+ defaultLocale = Locale.getDefault();
+ locale = new Locale("bg");
+ Locale.setDefault(locale);
+ }
+
+ @Test
+ public void testCenturiesFromNow() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0L), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("след 3 века", t.format(3155692597470L * 3L));
+ }
+
+ @Test
+ public void testCenturiesAgo() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(3155692597470L * 3L), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("преди 3 века", t.format(0));
+ }
+
+ @Test
+ public void testCenturySingular() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(3155692597470L), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("преди 1 век", t.format(0));
+ }
+
+ @Test
+ public void testDaysFromNow() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("след 3 дни", t.format(1000 * 60 * 60 * 24 * 3));
+ }
+
+ @Test
+ public void testDaysAgo() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 3), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("преди 3 дни", t.format(0));
+ }
+
+ @Test
+ public void testDaySingular() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("преди 1 ден", t.format(0));
+ }
+
+ @Test
+ public void testDecadesAgo() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(315569259747L * 3L), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("преди 3 десетилетия", t.format(0));
+ }
+
+ @Test
+ public void testDecadesFromNow() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("след 3 десетилетия", t.format(315569259747L * 3L));
+ }
+
+ @Test
+ public void testDecadeSingular() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("след 1 десетилетие", t.format(315569259747L));
+ }
+
+ @Test
+ public void testHoursFromNow() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("след 3 часа", t.format(1000 * 60 * 60 * 3));
+ }
+
+ @Test
+ public void testHoursAgo() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 3), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("преди 3 часа", t.format(0));
+ }
+
+ @Test
+ public void testHourSingular() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("преди 1 час", t.format(0));
+ }
+
+ @Test
+ public void testRightNow() throws Exception {
+ PrettyTime t = new PrettyTime();
+ assertEquals("в момента", t.format(LocalDateTime.now()));
+ }
+
+ @Test
+ public void testMomentsAgo() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(6000), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("току що", t.format(0));
+ }
+
+ @Test
+ public void testMinutesFromNow() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("след 12 минути", t.format(1000 * 60 * 12));
+ }
+
+ @Test
+ public void testMinutesAgo() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 12), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("преди 12 минути", t.format(0));
+ }
+
+ @Test
+ public void testMonthsFromNow() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("след 3 месеца", t.format((2629743830L * 3L)));
+ }
+
+ @Test
+ public void testMonthsAgo() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(2629743830L * 3L), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("преди 3 месеца", t.format(0));
+ }
+
+ @Test
+ public void testMonthSingular() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(2629743830L), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("преди 1 месец", t.format(0));
+ }
+
+ @Test
+ public void testWeeksFromNow() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("след 3 седмици", t.format(1000 * 60 * 60 * 24 * 7 * 3));
+ }
+
+ @Test
+ public void testWeeksAgo() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 7 * 3), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("преди 3 седмици", t.format(0));
+ }
+
+ @Test
+ public void testWeekSingular() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 7), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("преди 1 седмица", t.format(0));
+ }
+
+ @Test
+ public void testYearsFromNow() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("след 3 години", t.format(2629743830L * 12L * 3L));
+ }
+
+ @Test
+ public void testYearsAgo() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(2629743830L * 12L * 3L), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("преди 3 години", t.format(0));
+ }
+
+ @Test
+ public void testYearSingular() throws Exception {
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(2629743830L * 12L), ZoneId.systemDefault());
+ PrettyTime t = new PrettyTime(localDateTime);
+ assertEquals("преди 1 година", t.format(0));
+ }
+
+ @Test
+ public void testFormattingDurationListInThePast() throws Exception {
+ PrettyTime t = new PrettyTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 3 + 1000 * 60 * 60 * 15 + 1000 * 60 * 38), ZoneId.systemDefault()));
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0L), ZoneId.systemDefault());
+ List timeUnitQuantities = t.calculatePreciseDuration(localDateTime);
+ assertEquals("преди 3 дни 15 часа 38 минути", t.format(timeUnitQuantities));
+ }
+
+ @Test
+ public void testFormattingDurationListInTheFuture() throws Exception {
+ PrettyTime t = new PrettyTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(0L), ZoneId.systemDefault()));
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 3 + 1000 * 60 * 60 * 15
+ + 1000 * 60 * 38), ZoneId.systemDefault());
+ List timeUnitQuantities = t.calculatePreciseDuration(localDateTime);
+ assertEquals("след 3 дни 15 часа 38 минути", t.format(timeUnitQuantities));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ Locale.setDefault(defaultLocale);
+ }
+
+}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_CA_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_CA_Test.java
index c9f5df5..73d20f1 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_CA_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_CA_Test.java
@@ -1,7 +1,7 @@
package org.xbib.time.pretty;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import java.time.Instant;
@@ -15,16 +15,14 @@ import static org.junit.Assert.assertTrue;
public class PrettyTimeI18n_CA_Test {
- protected static Locale locale;
+ private Locale defaultLocale;
- @BeforeClass
- public static void setUp() throws Exception {
- locale = Locale.getDefault();
- Locale.setDefault(new Locale("ca"));
- }
+ private Locale locale;
- @AfterClass
- public static void tearDown() throws Exception {
+ @Before
+ public void setUp() throws Exception {
+ defaultLocale = Locale.getDefault();
+ locale = new Locale("ca");
Locale.setDefault(locale);
}
@@ -230,4 +228,8 @@ public class PrettyTimeI18n_CA_Test {
assertEquals("vor 3 Jahrzehnten", t.format((0)));
}
+ @After
+ public void tearDown() throws Exception {
+ Locale.setDefault(defaultLocale);
+ }
}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_CS_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_CS_Test.java
index 5baf377..90c24a1 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_CS_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_CS_Test.java
@@ -1,8 +1,8 @@
package org.xbib.time.pretty;
-import org.junit.AfterClass;
+import org.junit.After;
import org.junit.Assert;
-import org.junit.BeforeClass;
+import org.junit.Before;
import org.junit.Test;
import org.xbib.time.pretty.units.JustNow;
import org.xbib.time.pretty.units.Month;
@@ -17,16 +17,15 @@ import java.util.Locale;
import static org.junit.Assert.assertEquals;
public class PrettyTimeI18n_CS_Test {
- private static Locale locale;
- @BeforeClass
- public static void setUp() throws Exception {
- locale = Locale.getDefault();
- Locale.setDefault(new Locale("cs"));
- }
+ private Locale defaultLocale;
- @AfterClass
- public static void tearDown() throws Exception {
+ private Locale locale;
+
+ @Before
+ public void setUp() throws Exception {
+ defaultLocale = Locale.getDefault();
+ locale = new Locale("cs");
Locale.setDefault(locale);
}
@@ -213,7 +212,7 @@ public class PrettyTimeI18n_CS_Test {
/**
* Tests formatApproximateDuration and by proxy, formatDuration.
*
- * @throws Exception
+ * @throws Exception exception
*/
@Test
public void testFormatApproximateDuration() throws Exception {
@@ -224,4 +223,8 @@ public class PrettyTimeI18n_CS_Test {
Assert.assertEquals("10 minutami", result);
}
+ @After
+ public void tearDown() throws Exception {
+ Locale.setDefault(defaultLocale);
+ }
}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_DA_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_DA_Test.java
index 0febb08..87bac2b 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_DA_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_DA_Test.java
@@ -1,155 +1,166 @@
-package org.xbib.time.pretty;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import java.time.LocalDateTime;
-import java.util.Locale;
-
-import static org.junit.Assert.assertEquals;
-
-public class PrettyTimeI18n_DA_Test {
- private Locale locale;
-
- @Before
- public void setUp() throws Exception {
- locale = new Locale("da");
- }
-
- @Test
- public void testPrettyTime() {
- PrettyTime p = new PrettyTime(locale);
- assertEquals("straks", p.format(LocalDateTime.now()));
- }
-
- @Test
- public void testPrettyTimeCenturies() {
- PrettyTime p = new PrettyTime(3155692597470L * 3L, locale);
- assertEquals("3 århundreder siden", p.format(0));
-
- p = new PrettyTime(0, locale);
- assertEquals("3 århundreder fra nu", p.format(3155692597470L * 3L));
- }
-
- @Test
- public void testCeilingInterval() throws Exception {
- LocalDateTime then = LocalDateTime.of(2009, 5, 20, 0, 0);
- LocalDateTime ref = LocalDateTime.of(2009, 6, 17, 0, 0);
- PrettyTime t = new PrettyTime(ref, locale);
- assertEquals("1 måned siden", t.format(then));
- }
-
- @Test
- public void testRightNow() throws Exception {
- PrettyTime t = new PrettyTime(locale);
- assertEquals("straks", t.format(LocalDateTime.now()));
- }
-
- @Test
- public void testRightNowVariance() throws Exception {
- PrettyTime t = new PrettyTime(0, locale);
- assertEquals("straks", t.format(600));
- }
-
- @Test
- public void testMinutesFromNow() throws Exception {
- PrettyTime t = new PrettyTime(0, locale);
- assertEquals("om 12 minutter", t.format(1000 * 60 * 12));
- }
-
- @Test
- public void testHoursFromNow() throws Exception {
- PrettyTime t = new PrettyTime(0, locale);
- assertEquals("om 3 timer", t.format(1000 * 60 * 60 * 3));
- }
-
- @Test
- public void testDaysFromNow() throws Exception {
- PrettyTime t = new PrettyTime(0, locale);
- assertEquals("om 3 dage", t.format(1000 * 60 * 60 * 24 * 3));
- }
-
- @Test
- public void testWeeksFromNow() throws Exception {
- PrettyTime t = new PrettyTime(0, locale);
- assertEquals("om 3 uger", t.format(1000 * 60 * 60 * 24 * 7 * 3));
- }
-
- @Test
- public void testMonthsFromNow() throws Exception {
- PrettyTime t = new PrettyTime(0, locale);
- assertEquals("om 3 måneder", t.format(2629743830L * 3L));
- }
-
- @Test
- public void testYearsFromNow() throws Exception {
- PrettyTime t = new PrettyTime(0, locale);
- assertEquals("om 3 år", t.format(2629743830L * 12L * 3L));
- }
-
- @Test
- public void testDecadesFromNow() throws Exception {
- PrettyTime t = new PrettyTime(0, locale);
- assertEquals("3 årtier fra nu", t.format(315569259747L * 3L));
- }
-
- @Test
- public void testCenturiesFromNow() throws Exception {
- PrettyTime t = new PrettyTime(0, locale);
- assertEquals("3 århundreder fra nu", t.format(3155692597470L * 3L));
- }
-
- @Test
- public void testMomentsAgo() throws Exception {
- PrettyTime t = new PrettyTime(6000, locale);
- assertEquals("et øjeblik siden", t.format(0));
- }
-
- @Test
- public void testMinutesAgo() throws Exception {
- PrettyTime t = new PrettyTime((1000 * 60 * 12), locale);
- assertEquals("12 minutter siden", t.format(0));
- }
-
- @Test
- public void testHoursAgo() throws Exception {
- PrettyTime t = new PrettyTime((1000 * 60 * 60 * 3), locale);
- assertEquals("3 timer siden", t.format(0));
- }
-
- @Test
- public void testDaysAgo() throws Exception {
- PrettyTime t = new PrettyTime((1000 * 60 * 60 * 24 * 3), locale);
- assertEquals("3 dage siden", t.format(0));
- }
-
- @Test
- public void testWeeksAgo() throws Exception {
- PrettyTime t = new PrettyTime((1000 * 60 * 60 * 24 * 7 * 3), locale);
- assertEquals("3 uger siden", t.format(0));
- }
-
- @Test
- public void testMonthsAgo() throws Exception {
- PrettyTime t = new PrettyTime((2629743830L * 3L), locale);
- assertEquals("3 måneder siden", t.format(0));
- }
-
- @Test
- public void testYearsAgo() throws Exception {
- PrettyTime t = new PrettyTime((2629743830L * 12L * 3L), locale);
- assertEquals("3 år siden", t.format(0));
- }
-
- @Test
- public void testDecadesAgo() throws Exception {
- PrettyTime t = new PrettyTime((315569259747L * 3L), locale);
- assertEquals("3 årtier siden", t.format(0));
- }
-
- @Test
- public void testCenturiesAgo() throws Exception {
- PrettyTime t = new PrettyTime((3155692597470L * 3L), locale);
- assertEquals("3 århundreder siden", t.format(0));
- }
-}
\ No newline at end of file
+package org.xbib.time.pretty;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.time.LocalDateTime;
+import java.util.Locale;
+
+import static org.junit.Assert.assertEquals;
+
+public class PrettyTimeI18n_DA_Test {
+
+ private Locale defaultLocale;
+
+ private Locale locale;
+
+ @Before
+ public void setUp() throws Exception {
+ defaultLocale = Locale.getDefault();
+ locale = new Locale("da");
+ Locale.setDefault(locale);
+ }
+
+ @Test
+ public void testPrettyTime() {
+ PrettyTime p = new PrettyTime(locale);
+ assertEquals("straks", p.format(LocalDateTime.now()));
+ }
+
+ @Test
+ public void testPrettyTimeCenturies() {
+ PrettyTime p = new PrettyTime(3155692597470L * 3L, locale);
+ assertEquals("3 århundreder siden", p.format(0));
+
+ p = new PrettyTime(0, locale);
+ assertEquals("3 århundreder fra nu", p.format(3155692597470L * 3L));
+ }
+
+ @Test
+ public void testCeilingInterval() throws Exception {
+ LocalDateTime then = LocalDateTime.of(2009, 5, 20, 0, 0);
+ LocalDateTime ref = LocalDateTime.of(2009, 6, 17, 0, 0);
+ PrettyTime t = new PrettyTime(ref, locale);
+ assertEquals("1 måned siden", t.format(then));
+ }
+
+ @Test
+ public void testRightNow() throws Exception {
+ PrettyTime t = new PrettyTime(locale);
+ assertEquals("straks", t.format(LocalDateTime.now()));
+ }
+
+ @Test
+ public void testRightNowVariance() throws Exception {
+ PrettyTime t = new PrettyTime(0, locale);
+ assertEquals("straks", t.format(600));
+ }
+
+ @Test
+ public void testMinutesFromNow() throws Exception {
+ PrettyTime t = new PrettyTime(0, locale);
+ assertEquals("om 12 minutter", t.format(1000 * 60 * 12));
+ }
+
+ @Test
+ public void testHoursFromNow() throws Exception {
+ PrettyTime t = new PrettyTime(0, locale);
+ assertEquals("om 3 timer", t.format(1000 * 60 * 60 * 3));
+ }
+
+ @Test
+ public void testDaysFromNow() throws Exception {
+ PrettyTime t = new PrettyTime(0, locale);
+ assertEquals("om 3 dage", t.format(1000 * 60 * 60 * 24 * 3));
+ }
+
+ @Test
+ public void testWeeksFromNow() throws Exception {
+ PrettyTime t = new PrettyTime(0, locale);
+ assertEquals("om 3 uger", t.format(1000 * 60 * 60 * 24 * 7 * 3));
+ }
+
+ @Test
+ public void testMonthsFromNow() throws Exception {
+ PrettyTime t = new PrettyTime(0, locale);
+ assertEquals("om 3 måneder", t.format(2629743830L * 3L));
+ }
+
+ @Test
+ public void testYearsFromNow() throws Exception {
+ PrettyTime t = new PrettyTime(0, locale);
+ assertEquals("om 3 år", t.format(2629743830L * 12L * 3L));
+ }
+
+ @Test
+ public void testDecadesFromNow() throws Exception {
+ PrettyTime t = new PrettyTime(0, locale);
+ assertEquals("3 årtier fra nu", t.format(315569259747L * 3L));
+ }
+
+ @Test
+ public void testCenturiesFromNow() throws Exception {
+ PrettyTime t = new PrettyTime(0, locale);
+ assertEquals("3 århundreder fra nu", t.format(3155692597470L * 3L));
+ }
+
+ @Test
+ public void testMomentsAgo() throws Exception {
+ PrettyTime t = new PrettyTime(6000, locale);
+ assertEquals("et øjeblik siden", t.format(0));
+ }
+
+ @Test
+ public void testMinutesAgo() throws Exception {
+ PrettyTime t = new PrettyTime((1000 * 60 * 12), locale);
+ assertEquals("12 minutter siden", t.format(0));
+ }
+
+ @Test
+ public void testHoursAgo() throws Exception {
+ PrettyTime t = new PrettyTime((1000 * 60 * 60 * 3), locale);
+ assertEquals("3 timer siden", t.format(0));
+ }
+
+ @Test
+ public void testDaysAgo() throws Exception {
+ PrettyTime t = new PrettyTime((1000 * 60 * 60 * 24 * 3), locale);
+ assertEquals("3 dage siden", t.format(0));
+ }
+
+ @Test
+ public void testWeeksAgo() throws Exception {
+ PrettyTime t = new PrettyTime((1000 * 60 * 60 * 24 * 7 * 3), locale);
+ assertEquals("3 uger siden", t.format(0));
+ }
+
+ @Test
+ public void testMonthsAgo() throws Exception {
+ PrettyTime t = new PrettyTime((2629743830L * 3L), locale);
+ assertEquals("3 måneder siden", t.format(0));
+ }
+
+ @Test
+ public void testYearsAgo() throws Exception {
+ PrettyTime t = new PrettyTime((2629743830L * 12L * 3L), locale);
+ assertEquals("3 år siden", t.format(0));
+ }
+
+ @Test
+ public void testDecadesAgo() throws Exception {
+ PrettyTime t = new PrettyTime((315569259747L * 3L), locale);
+ assertEquals("3 årtier siden", t.format(0));
+ }
+
+ @Test
+ public void testCenturiesAgo() throws Exception {
+ PrettyTime t = new PrettyTime((3155692597470L * 3L), locale);
+ assertEquals("3 århundreder siden", t.format(0));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ Locale.setDefault(defaultLocale);
+ }
+}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_ET_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_ET_Test.java
index 546f3dd..0eaf932 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_ET_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_ET_Test.java
@@ -1,5 +1,6 @@
package org.xbib.time.pretty;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.xbib.time.pretty.units.JustNow;
@@ -14,11 +15,16 @@ import java.util.Locale;
import static org.junit.Assert.assertEquals;
public class PrettyTimeI18n_ET_Test {
+
+ private Locale defaultLocale;
+
private Locale locale;
@Before
public void setUp() throws Exception {
+ defaultLocale = Locale.getDefault();
locale = new Locale("et");
+ Locale.setDefault(locale);
}
@Test
@@ -333,4 +339,8 @@ public class PrettyTimeI18n_ET_Test {
return t;
}
+ @After
+ public void tearDown() throws Exception {
+ Locale.setDefault(defaultLocale);
+ }
}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_FA_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_FA_Test.java
index dc96683..a476989 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_FA_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_FA_Test.java
@@ -1,239 +1,238 @@
-package org.xbib.time.pretty;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.util.List;
-import java.util.Locale;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-public class PrettyTimeI18n_FA_Test {
-
- // Stores current locale so that it can be restored
- private Locale locale;
-
- // Method setUp() is called automatically before every test method
- @Before
- public void setUp() throws Exception {
- locale = new Locale("fa");
- Locale.setDefault(locale);
- }
-
- @Test
- public void testCeilingInterval() throws Exception {
- LocalDateTime then = LocalDateTime.of(2012, 5, 20, 0, 0);
- LocalDateTime ref = LocalDateTime.of(2012, 6, 17, 0, 0);
- PrettyTime t = new PrettyTime(ref);
- assertEquals("1 ماه پیش", t.format(then));
- }
-
- @Test
- public void testRightNow() throws Exception {
- PrettyTime t = new PrettyTime();
- assertEquals("چند لحظه دیگر", t.format(LocalDateTime.now()));
- }
-
- @Test
- public void testRightNowVariance() throws Exception {
- PrettyTime t = new PrettyTime(0);
- assertEquals("چند لحظه دیگر", t.format((600)));
- }
-
- @Test
- public void testMinutesFromNow() throws Exception {
- PrettyTime t = new PrettyTime(0);
- assertEquals("12 دقیقه دیگر", t.format((1000 * 60 * 12)));
- }
-
- @Test
- public void testHoursFromNow() throws Exception {
- PrettyTime t = new PrettyTime(0);
- assertEquals("3 ساعت دیگر", t.format((1000 * 60 * 60 * 3)));
- }
-
- @Test
- public void testDaysFromNow() throws Exception {
- PrettyTime t = new PrettyTime(0);
- assertEquals("3 روز دیگر", t.format((1000 * 60 * 60 * 24 * 3)));
- }
-
- @Test
- public void testWeeksFromNow() throws Exception {
- PrettyTime t = new PrettyTime(0);
- assertEquals("3 هفته دیگر", t.format((1000 * 60 * 60 * 24 * 7 * 3)));
- }
-
- @Test
- public void testMonthsFromNow() throws Exception {
- PrettyTime t = new PrettyTime(0);
- assertEquals("3 ماه دیگر", t.format((2629743830L * 3L)));
- }
-
- @Test
- public void testYearsFromNow() throws Exception {
- PrettyTime t = new PrettyTime(0);
- assertEquals("3 سال دیگر", t.format((2629743830L * 12L * 3L)));
- }
-
- @Test
- public void testDecadesFromNow() throws Exception {
- PrettyTime t = new PrettyTime(0);
- assertEquals("3 دهه دیگر", t.format((315569259747L * 3L)));
- }
-
- @Test
- public void testCenturiesFromNow() throws Exception {
- PrettyTime t = new PrettyTime(0);
- assertEquals("3 قرن دیگر", t.format((3155692597470L * 3L)));
- }
-
- /*
- * Past
- */
- @Test
- public void testMomentsAgo() throws Exception {
- PrettyTime t = new PrettyTime(6000);
- assertEquals("چند لحظه پیش", t.format((0)));
- }
-
- @Test
- public void testMinutesAgo() throws Exception {
- PrettyTime t = new PrettyTime(1000 * 60 * 12);
- assertEquals("12 دقیقه پیش", t.format((0)));
- }
-
- @Test
- public void testHoursAgo() throws Exception {
- PrettyTime t = new PrettyTime(1000 * 60 * 60 * 3);
- assertEquals("3 ساعت پیش", t.format((0)));
- }
-
- @Test
- public void testDaysAgo() throws Exception {
- PrettyTime t = new PrettyTime(1000 * 60 * 60 * 24 * 3);
- assertEquals("3 روز پیش", t.format((0)));
- }
-
- @Test
- public void testWeeksAgo() throws Exception {
- PrettyTime t = new PrettyTime(1000 * 60 * 60 * 24 * 7 * 3);
- assertEquals("3 هفته پیش", t.format((0)));
- }
-
- @Test
- public void testMonthsAgo() throws Exception {
- PrettyTime t = new PrettyTime(2629743830L * 3L);
- assertEquals("3 ماه پیش", t.format((0)));
- }
-
- @Test
- public void testCustomFormat() throws Exception {
- PrettyTime t = new PrettyTime(0);
- TimeUnit unit = new TimeUnit() {
- @Override
- public long getMaxQuantity() {
- return 0;
- }
-
- @Override
- public long getMillisPerUnit() {
- return 5000;
- }
- };
- t.clearUnits();
- t.registerUnit(unit, new SimpleTimeFormat()
- .setSingularName("tick").setPluralName("ticks")
- .setPattern("%n %u").setRoundingTolerance(20)
- .setFutureSuffix("... RUN!")
- .setFuturePrefix("self destruct in: ").setPastPrefix("self destruct was: ").setPastSuffix(
- " ago..."));
-
- assertEquals("self destruct in: 5 ticks ... RUN!", t.format((25000)));
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(25000), ZoneId.systemDefault());
- t.setReference(localDateTime);
- assertEquals("self destruct was: 5 ticks ago...", t.format((0)));
- }
-
- @Test
- public void testYearsAgo() throws Exception {
- PrettyTime t = new PrettyTime(2629743830L * 12L * 3L);
- assertEquals("3 سال پیش", t.format((0)));
- }
-
- @Test
- public void testDecadesAgo() throws Exception {
- PrettyTime t = new PrettyTime(315569259747L * 3L);
- assertEquals("3 دهه پیش", t.format((0)));
- }
-
- @Test
- public void testCenturiesAgo() throws Exception {
- PrettyTime t = new PrettyTime(3155692597470L * 3L);
- assertEquals("3 قرن پیش", t.format((0)));
- }
-
- @Test
- public void testWithinTwoHoursRounding() throws Exception {
- PrettyTime t = new PrettyTime();
- LocalDateTime localDateTime = LocalDateTime.now().minusSeconds(6543);
- assertEquals("2 ساعت پیش", t.format(localDateTime));
- }
-
- @Test
- public void testPreciseInTheFuture() throws Exception {
- PrettyTime t = new PrettyTime();
- LocalDateTime localDateTime = LocalDateTime.now().plusSeconds(10 * 60 + 5 * 60 * 60);
- List timeUnitQuantities = t.calculatePreciseDuration(localDateTime);
- assertTrue(timeUnitQuantities.size() >= 2); // might be more because of milliseconds between date capturing and result
- // calculation
- assertEquals(5, timeUnitQuantities.get(0).getQuantity());
- assertEquals(10, timeUnitQuantities.get(1).getQuantity());
- }
-
- @Test
- public void testPreciseInThePast() throws Exception {
- PrettyTime t = new PrettyTime();
- LocalDateTime localDateTime = LocalDateTime.now().minusSeconds(10 * 60 + 5 * 60 * 60);
- List timeUnitQuantities = t.calculatePreciseDuration(localDateTime);
- assertTrue(timeUnitQuantities.size() >= 2); // might be more because of milliseconds between date capturing and result
- // calculation
- assertEquals(-5, timeUnitQuantities.get(0).getQuantity());
- assertTrue(-10 == timeUnitQuantities.get(1).getQuantity() || -9 == timeUnitQuantities.get(1).getQuantity());
- }
-
- @Test
- public void testFormattingDurationListInThePast() throws Exception {
- PrettyTime t = new PrettyTime(1000 * 60 * 60 * 24 * 3 + 1000 * 60 * 60 * 15 + 1000 * 60 * 38);
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- List timeUnitQuantities = t.calculatePreciseDuration(localDateTime);
- assertEquals("3 روز 15 ساعت 38 دقیقه پیش", t.format(timeUnitQuantities));
- }
-
- @Test
- public void testFormattingDurationListInTheFuture() throws Exception {
- PrettyTime t = new PrettyTime(0);
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 3 + 1000 * 60 * 60 * 15
- + 1000 * 60 * 38), ZoneId.systemDefault());
- List timeUnitQuantities = t.calculatePreciseDuration(localDateTime);
- assertEquals("3 روز 15 ساعت 38 دقیقه دیگر", t.format(timeUnitQuantities));
- }
-
- @Test
- public void testSetLocale() throws Exception {
- PrettyTime t = new PrettyTime(315569259747L * 3L);
- assertEquals("3 دهه پیش", t.format((0)));
- }
-
- // Method tearDown() is called automatically after every test method
- @After
- public void tearDown() throws Exception {
- Locale.setDefault(locale);
- }
-
-}
+package org.xbib.time.pretty;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.List;
+import java.util.Locale;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class PrettyTimeI18n_FA_Test {
+
+ private Locale defaultLocale;
+
+ private Locale locale;
+
+ @Before
+ public void setUp() throws Exception {
+ defaultLocale = Locale.getDefault();
+ locale = new Locale("fa");
+ Locale.setDefault(locale);
+ }
+
+ @Test
+ public void testCeilingInterval() throws Exception {
+ LocalDateTime then = LocalDateTime.of(2012, 5, 20, 0, 0);
+ LocalDateTime ref = LocalDateTime.of(2012, 6, 17, 0, 0);
+ PrettyTime t = new PrettyTime(ref);
+ assertEquals("1 ماه پیش", t.format(then));
+ }
+
+ @Test
+ public void testRightNow() throws Exception {
+ PrettyTime t = new PrettyTime();
+ assertEquals("چند لحظه دیگر", t.format(LocalDateTime.now()));
+ }
+
+ @Test
+ public void testRightNowVariance() throws Exception {
+ PrettyTime t = new PrettyTime(0);
+ assertEquals("چند لحظه دیگر", t.format((600)));
+ }
+
+ @Test
+ public void testMinutesFromNow() throws Exception {
+ PrettyTime t = new PrettyTime(0);
+ assertEquals("12 دقیقه دیگر", t.format((1000 * 60 * 12)));
+ }
+
+ @Test
+ public void testHoursFromNow() throws Exception {
+ PrettyTime t = new PrettyTime(0);
+ assertEquals("3 ساعت دیگر", t.format((1000 * 60 * 60 * 3)));
+ }
+
+ @Test
+ public void testDaysFromNow() throws Exception {
+ PrettyTime t = new PrettyTime(0);
+ assertEquals("3 روز دیگر", t.format((1000 * 60 * 60 * 24 * 3)));
+ }
+
+ @Test
+ public void testWeeksFromNow() throws Exception {
+ PrettyTime t = new PrettyTime(0);
+ assertEquals("3 هفته دیگر", t.format((1000 * 60 * 60 * 24 * 7 * 3)));
+ }
+
+ @Test
+ public void testMonthsFromNow() throws Exception {
+ PrettyTime t = new PrettyTime(0);
+ assertEquals("3 ماه دیگر", t.format((2629743830L * 3L)));
+ }
+
+ @Test
+ public void testYearsFromNow() throws Exception {
+ PrettyTime t = new PrettyTime(0);
+ assertEquals("3 سال دیگر", t.format((2629743830L * 12L * 3L)));
+ }
+
+ @Test
+ public void testDecadesFromNow() throws Exception {
+ PrettyTime t = new PrettyTime(0);
+ assertEquals("3 دهه دیگر", t.format((315569259747L * 3L)));
+ }
+
+ @Test
+ public void testCenturiesFromNow() throws Exception {
+ PrettyTime t = new PrettyTime(0);
+ assertEquals("3 قرن دیگر", t.format((3155692597470L * 3L)));
+ }
+
+ /*
+ * Past
+ */
+ @Test
+ public void testMomentsAgo() throws Exception {
+ PrettyTime t = new PrettyTime(6000);
+ assertEquals("چند لحظه پیش", t.format((0)));
+ }
+
+ @Test
+ public void testMinutesAgo() throws Exception {
+ PrettyTime t = new PrettyTime(1000 * 60 * 12);
+ assertEquals("12 دقیقه پیش", t.format((0)));
+ }
+
+ @Test
+ public void testHoursAgo() throws Exception {
+ PrettyTime t = new PrettyTime(1000 * 60 * 60 * 3);
+ assertEquals("3 ساعت پیش", t.format((0)));
+ }
+
+ @Test
+ public void testDaysAgo() throws Exception {
+ PrettyTime t = new PrettyTime(1000 * 60 * 60 * 24 * 3);
+ assertEquals("3 روز پیش", t.format((0)));
+ }
+
+ @Test
+ public void testWeeksAgo() throws Exception {
+ PrettyTime t = new PrettyTime(1000 * 60 * 60 * 24 * 7 * 3);
+ assertEquals("3 هفته پیش", t.format((0)));
+ }
+
+ @Test
+ public void testMonthsAgo() throws Exception {
+ PrettyTime t = new PrettyTime(2629743830L * 3L);
+ assertEquals("3 ماه پیش", t.format((0)));
+ }
+
+ @Test
+ public void testCustomFormat() throws Exception {
+ PrettyTime t = new PrettyTime(0);
+ TimeUnit unit = new TimeUnit() {
+ @Override
+ public long getMaxQuantity() {
+ return 0;
+ }
+
+ @Override
+ public long getMillisPerUnit() {
+ return 5000;
+ }
+ };
+ t.clearUnits();
+ t.registerUnit(unit, new SimpleTimeFormat()
+ .setSingularName("tick").setPluralName("ticks")
+ .setPattern("%n %u").setRoundingTolerance(20)
+ .setFutureSuffix("... RUN!")
+ .setFuturePrefix("self destruct in: ").setPastPrefix("self destruct was: ").setPastSuffix(
+ " ago..."));
+
+ assertEquals("self destruct in: 5 ticks ... RUN!", t.format((25000)));
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(25000), ZoneId.systemDefault());
+ t.setReference(localDateTime);
+ assertEquals("self destruct was: 5 ticks ago...", t.format((0)));
+ }
+
+ @Test
+ public void testYearsAgo() throws Exception {
+ PrettyTime t = new PrettyTime(2629743830L * 12L * 3L);
+ assertEquals("3 سال پیش", t.format((0)));
+ }
+
+ @Test
+ public void testDecadesAgo() throws Exception {
+ PrettyTime t = new PrettyTime(315569259747L * 3L);
+ assertEquals("3 دهه پیش", t.format((0)));
+ }
+
+ @Test
+ public void testCenturiesAgo() throws Exception {
+ PrettyTime t = new PrettyTime(3155692597470L * 3L);
+ assertEquals("3 قرن پیش", t.format((0)));
+ }
+
+ @Test
+ public void testWithinTwoHoursRounding() throws Exception {
+ PrettyTime t = new PrettyTime();
+ LocalDateTime localDateTime = LocalDateTime.now().minusSeconds(6543);
+ assertEquals("2 ساعت پیش", t.format(localDateTime));
+ }
+
+ @Test
+ public void testPreciseInTheFuture() throws Exception {
+ PrettyTime t = new PrettyTime();
+ LocalDateTime localDateTime = LocalDateTime.now().plusSeconds(10 * 60 + 5 * 60 * 60);
+ List timeUnitQuantities = t.calculatePreciseDuration(localDateTime);
+ assertTrue(timeUnitQuantities.size() >= 2); // might be more because of milliseconds between date capturing and result
+ // calculation
+ assertEquals(5, timeUnitQuantities.get(0).getQuantity());
+ assertEquals(10, timeUnitQuantities.get(1).getQuantity());
+ }
+
+ @Test
+ public void testPreciseInThePast() throws Exception {
+ PrettyTime t = new PrettyTime();
+ LocalDateTime localDateTime = LocalDateTime.now().minusSeconds(10 * 60 + 5 * 60 * 60);
+ List timeUnitQuantities = t.calculatePreciseDuration(localDateTime);
+ assertTrue(timeUnitQuantities.size() >= 2); // might be more because of milliseconds between date capturing and result
+ // calculation
+ assertEquals(-5, timeUnitQuantities.get(0).getQuantity());
+ assertTrue(-10 == timeUnitQuantities.get(1).getQuantity() || -9 == timeUnitQuantities.get(1).getQuantity());
+ }
+
+ @Test
+ public void testFormattingDurationListInThePast() throws Exception {
+ PrettyTime t = new PrettyTime(1000 * 60 * 60 * 24 * 3 + 1000 * 60 * 60 * 15 + 1000 * 60 * 38);
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ List timeUnitQuantities = t.calculatePreciseDuration(localDateTime);
+ assertEquals("3 روز 15 ساعت 38 دقیقه پیش", t.format(timeUnitQuantities));
+ }
+
+ @Test
+ public void testFormattingDurationListInTheFuture() throws Exception {
+ PrettyTime t = new PrettyTime(0);
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1000 * 60 * 60 * 24 * 3 + 1000 * 60 * 60 * 15
+ + 1000 * 60 * 38), ZoneId.systemDefault());
+ List timeUnitQuantities = t.calculatePreciseDuration(localDateTime);
+ assertEquals("3 روز 15 ساعت 38 دقیقه دیگر", t.format(timeUnitQuantities));
+ }
+
+ @Test
+ public void testSetLocale() throws Exception {
+ PrettyTime t = new PrettyTime(315569259747L * 3L);
+ assertEquals("3 دهه پیش", t.format((0)));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ Locale.setDefault(defaultLocale);
+ }
+}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_FI_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_FI_Test.java
index 7fd0d44..f708a24 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_FI_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_FI_Test.java
@@ -1,5 +1,6 @@
package org.xbib.time.pretty;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.xbib.time.pretty.units.JustNow;
@@ -14,11 +15,16 @@ import java.util.Locale;
import static org.junit.Assert.assertEquals;
public class PrettyTimeI18n_FI_Test {
+
+ private Locale defaultLocale;
+
private Locale locale;
@Before
public void setUp() throws Exception {
+ defaultLocale = Locale.getDefault();
locale = new Locale("fi");
+ Locale.setDefault(locale);
}
@Test
@@ -333,4 +339,9 @@ public class PrettyTimeI18n_FI_Test {
}
return t;
}
+
+ @After
+ public void tearDown() throws Exception {
+ Locale.setDefault(defaultLocale);
+ }
}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_FR_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_FR_Test.java
index 73a5927..cdfe3c9 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_FR_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_FR_Test.java
@@ -12,37 +12,34 @@ import java.util.Locale;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-/**
- * All the tests for PrettyTime.
- */
public class PrettyTimeI18n_FR_Test {
- // Stores current locale so that it can be restored
+ private Locale defaultLocale;
+
private Locale locale;
- // Method setUp() is called automatically before every test method
@Before
public void setUp() throws Exception {
- locale = Locale.getDefault();
+ defaultLocale = Locale.getDefault();
+ locale = Locale.FRENCH;
+ Locale.setDefault(locale);
}
@Test
public void testPrettyTimeFRENCH() {
// The FRENCH resource bundle should be used
- PrettyTime p = new PrettyTime(Locale.FRENCH);
+ PrettyTime p = new PrettyTime(locale);
assertEquals("à l'instant", p.format(LocalDateTime.now()));
}
@Test
public void testPrettyTimeFRENCHCenturies() {
- PrettyTime p = new PrettyTime((3155692597470L * 3L), Locale.FRENCH);
+ PrettyTime p = new PrettyTime((3155692597470L * 3L), locale);
assertEquals(p.format(0), "il y a 3 siècles");
}
@Test
public void testPrettyTimeViaDefaultLocaleFRENCH() {
- // The FRENCH resource bundle should be used
- Locale.setDefault(Locale.FRENCH);
PrettyTime p = new PrettyTime();
assertEquals(p.format(LocalDateTime.now()), "à l'instant");
}
@@ -50,7 +47,7 @@ public class PrettyTimeI18n_FR_Test {
@Test
public void testPrettyTimeFRENCHLocale() {
long t = 1L;
- PrettyTime p = new PrettyTime((0), Locale.FRENCH);
+ PrettyTime p = new PrettyTime((0), locale);
while (1000L * 60L * 60L * 24L * 365L * 1000000L > t) {
LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
assertTrue(p.format(localDateTime).startsWith("dans") || p.format(localDateTime).startsWith("à l'instant"));
@@ -58,10 +55,9 @@ public class PrettyTimeI18n_FR_Test {
}
}
- // Method tearDown() is called automatically after every test method
@After
public void tearDown() throws Exception {
- Locale.setDefault(locale);
+ Locale.setDefault(defaultLocale);
}
}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_IT_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_IT_Test.java
index 24df2ff..2e96182 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_IT_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_IT_Test.java
@@ -1,5 +1,6 @@
package org.xbib.time.pretty;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.xbib.time.pretty.units.JustNow;
@@ -14,11 +15,16 @@ import java.util.Locale;
import static org.junit.Assert.assertEquals;
public class PrettyTimeI18n_IT_Test {
+
+ private Locale defaultLocale;
+
private Locale locale;
@Before
public void setUp() throws Exception {
+ defaultLocale = Locale.getDefault();
locale = new Locale("it");
+ Locale.setDefault(locale);
}
@Test
@@ -333,4 +339,9 @@ public class PrettyTimeI18n_IT_Test {
}
return t;
}
+
+ @After
+ public void tearDown() throws Exception {
+ Locale.setDefault(defaultLocale);
+ }
}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_KO_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_KO_Test.java
index 8e1607e..e5d51cc 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_KO_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_KO_Test.java
@@ -15,13 +15,15 @@ import static org.junit.Assert.assertTrue;
public class PrettyTimeI18n_KO_Test {
+ private Locale defaultLocale;
+
private Locale locale;
- // Method setUp() is called automatically before every test method
@Before
public void setUp() throws Exception {
- locale = Locale.getDefault();
- Locale.setDefault(Locale.KOREA);
+ defaultLocale = Locale.getDefault();
+ locale = Locale.KOREA;
+ Locale.setDefault(locale);
}
@Test
@@ -227,10 +229,8 @@ public class PrettyTimeI18n_KO_Test {
assertEquals("vor 3 Jahrzehnten", t.format((0)));
}
- // Method tearDown() is called automatically after every test method
@After
public void tearDown() throws Exception {
- Locale.setDefault(locale);
+ Locale.setDefault(defaultLocale);
}
-
}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_NL_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_NL_Test.java
index 86f19bc..b25f2d5 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_NL_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_NL_Test.java
@@ -1,5 +1,6 @@
package org.xbib.time.pretty;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -11,11 +12,16 @@ import java.util.Locale;
import static org.junit.Assert.assertEquals;
public class PrettyTimeI18n_NL_Test {
+
+ private Locale defaultLocale;
+
private Locale locale;
@Before
public void setUp() throws Exception {
+ defaultLocale = Locale.getDefault();
locale = new Locale("nl");
+ Locale.setDefault(locale);
}
@Test
@@ -159,4 +165,9 @@ public class PrettyTimeI18n_NL_Test {
PrettyTime t = new PrettyTime((3155692597470L * 3L), locale);
assertEquals("3 eeuwen geleden", t.format((0)));
}
-}
\ No newline at end of file
+
+ @After
+ public void tearDown() throws Exception {
+ Locale.setDefault(defaultLocale);
+ }
+}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_NO_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_NO_Test.java
index 364e9fe..68fd08e 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_NO_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_NO_Test.java
@@ -1,163 +1,173 @@
-package org.xbib.time.pretty;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.util.Locale;
-
-import static org.junit.Assert.assertEquals;
-
-public class PrettyTimeI18n_NO_Test {
-
- private Locale locale;
-
- @Before
- public void setUp() throws Exception {
- locale = new Locale("no");
- }
-
- @Test
- public void testPrettyTime() {
- PrettyTime p = new PrettyTime(locale);
- assertEquals("straks", p.format(LocalDateTime.now()));
- }
-
- @Test
- public void testPrettyTimeCenturies() {
- PrettyTime p = new PrettyTime((3155692597470L * 3L), locale);
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- assertEquals("3 århundre siden", p.format(localDateTime));
-
- p = new PrettyTime(0, locale);
- localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(3155692597470L * 3L), ZoneId.systemDefault());
- assertEquals("3 århundre fra nå", p.format(localDateTime));
- }
-
- @Test
- public void testCeilingInterval() throws Exception {
- LocalDateTime then = LocalDateTime.of(2009, 5, 20, 0, 0);
- LocalDateTime ref = LocalDateTime.of(2009, 6, 17, 0, 0);
- PrettyTime t = new PrettyTime(ref, locale);
- assertEquals("1 måned siden", t.format(then));
- }
-
- @Test
- public void testRightNow() throws Exception {
- PrettyTime t = new PrettyTime(locale);
- assertEquals("straks", t.format(LocalDateTime.now()));
- }
-
- @Test
- public void testRightNowVariance() throws Exception {
- PrettyTime t = new PrettyTime((0), locale);
- assertEquals("straks", t.format((600)));
- }
-
- @Test
- public void testMinutesFromNow() throws Exception {
- PrettyTime t = new PrettyTime((0), locale);
- assertEquals("om 12 minutter", t.format((1000 * 60 * 12)));
- }
-
- @Test
- public void testHoursFromNow() throws Exception {
- PrettyTime t = new PrettyTime((0), locale);
- assertEquals("om 3 timer", t.format((1000 * 60 * 60 * 3)));
- }
-
- @Test
- public void testDaysFromNow() throws Exception {
- PrettyTime t = new PrettyTime((0), locale);
- assertEquals("om 3 dager", t.format((1000 * 60 * 60 * 24 * 3)));
- }
-
- @Test
- public void testWeeksFromNow() throws Exception {
- PrettyTime t = new PrettyTime((0), locale);
- assertEquals("om 3 uker", t.format((1000 * 60 * 60 * 24 * 7 * 3)));
- }
-
- @Test
- public void testMonthsFromNow() throws Exception {
- PrettyTime t = new PrettyTime((0), locale);
- assertEquals("om 3 måneder", t.format((2629743830L * 3L)));
- }
-
- @Test
- public void testYearsFromNow() throws Exception {
- PrettyTime t = new PrettyTime((0), locale);
- assertEquals("om 3 år", t.format((2629743830L * 12L * 3L)));
- }
-
- @Test
- public void testDecadesFromNow() throws Exception {
- PrettyTime t = new PrettyTime((0), locale);
- assertEquals("3 tiår fra nå", t.format((315569259747L * 3L)));
- }
-
- @Test
- public void testCenturiesFromNow() throws Exception {
- PrettyTime t = new PrettyTime((0), locale);
- assertEquals("3 århundre fra nå", t.format((3155692597470L * 3L)));
- }
-
- /*
- * Past
- */
- @Test
- public void testMomentsAgo() throws Exception {
- PrettyTime t = new PrettyTime((6000), locale);
- assertEquals("et øyeblikk siden", t.format((0)));
- }
-
- @Test
- public void testMinutesAgo() throws Exception {
- PrettyTime t = new PrettyTime((1000 * 60 * 12), locale);
- assertEquals("12 minutter siden", t.format((0)));
- }
-
- @Test
- public void testHoursAgo() throws Exception {
- PrettyTime t = new PrettyTime((1000 * 60 * 60 * 3), locale);
- assertEquals("3 timer siden", t.format((0)));
- }
-
- @Test
- public void testDaysAgo() throws Exception {
- PrettyTime t = new PrettyTime((1000 * 60 * 60 * 24 * 3), locale);
- assertEquals("3 dager siden", t.format((0)));
- }
-
- @Test
- public void testWeeksAgo() throws Exception {
- PrettyTime t = new PrettyTime((1000 * 60 * 60 * 24 * 7 * 3), locale);
- assertEquals("3 uker siden", t.format((0)));
- }
-
- @Test
- public void testMonthsAgo() throws Exception {
- PrettyTime t = new PrettyTime((2629743830L * 3L), locale);
- assertEquals("3 måneder siden", t.format((0)));
- }
-
- @Test
- public void testYearsAgo() throws Exception {
- PrettyTime t = new PrettyTime((2629743830L * 12L * 3L), locale);
- assertEquals("3 år siden", t.format((0)));
- }
-
- @Test
- public void testDecadesAgo() throws Exception {
- PrettyTime t = new PrettyTime((315569259747L * 3L), locale);
- assertEquals("3 tiår siden", t.format((0)));
- }
-
- @Test
- public void testCenturiesAgo() throws Exception {
- PrettyTime t = new PrettyTime((3155692597470L * 3L), locale);
- assertEquals("3 århundre siden", t.format((0)));
- }
-}
\ No newline at end of file
+package org.xbib.time.pretty;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Locale;
+
+import static org.junit.Assert.assertEquals;
+
+public class PrettyTimeI18n_NO_Test {
+
+ private Locale defaultLocale;
+
+ private Locale locale;
+
+ @Before
+ public void setUp() throws Exception {
+ defaultLocale = Locale.getDefault();
+ locale = new Locale("no");
+ Locale.setDefault(locale);
+ }
+
+ @Test
+ public void testPrettyTime() {
+ PrettyTime p = new PrettyTime(locale);
+ assertEquals("straks", p.format(LocalDateTime.now()));
+ }
+
+ @Test
+ public void testPrettyTimeCenturies() {
+ PrettyTime p = new PrettyTime((3155692597470L * 3L), locale);
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ assertEquals("3 århundre siden", p.format(localDateTime));
+
+ p = new PrettyTime(0, locale);
+ localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(3155692597470L * 3L), ZoneId.systemDefault());
+ assertEquals("3 århundre fra nå", p.format(localDateTime));
+ }
+
+ @Test
+ public void testCeilingInterval() throws Exception {
+ LocalDateTime then = LocalDateTime.of(2009, 5, 20, 0, 0);
+ LocalDateTime ref = LocalDateTime.of(2009, 6, 17, 0, 0);
+ PrettyTime t = new PrettyTime(ref, locale);
+ assertEquals("1 måned siden", t.format(then));
+ }
+
+ @Test
+ public void testRightNow() throws Exception {
+ PrettyTime t = new PrettyTime(locale);
+ assertEquals("straks", t.format(LocalDateTime.now()));
+ }
+
+ @Test
+ public void testRightNowVariance() throws Exception {
+ PrettyTime t = new PrettyTime((0), locale);
+ assertEquals("straks", t.format((600)));
+ }
+
+ @Test
+ public void testMinutesFromNow() throws Exception {
+ PrettyTime t = new PrettyTime((0), locale);
+ assertEquals("om 12 minutter", t.format((1000 * 60 * 12)));
+ }
+
+ @Test
+ public void testHoursFromNow() throws Exception {
+ PrettyTime t = new PrettyTime((0), locale);
+ assertEquals("om 3 timer", t.format((1000 * 60 * 60 * 3)));
+ }
+
+ @Test
+ public void testDaysFromNow() throws Exception {
+ PrettyTime t = new PrettyTime((0), locale);
+ assertEquals("om 3 dager", t.format((1000 * 60 * 60 * 24 * 3)));
+ }
+
+ @Test
+ public void testWeeksFromNow() throws Exception {
+ PrettyTime t = new PrettyTime((0), locale);
+ assertEquals("om 3 uker", t.format((1000 * 60 * 60 * 24 * 7 * 3)));
+ }
+
+ @Test
+ public void testMonthsFromNow() throws Exception {
+ PrettyTime t = new PrettyTime((0), locale);
+ assertEquals("om 3 måneder", t.format((2629743830L * 3L)));
+ }
+
+ @Test
+ public void testYearsFromNow() throws Exception {
+ PrettyTime t = new PrettyTime((0), locale);
+ assertEquals("om 3 år", t.format((2629743830L * 12L * 3L)));
+ }
+
+ @Test
+ public void testDecadesFromNow() throws Exception {
+ PrettyTime t = new PrettyTime((0), locale);
+ assertEquals("3 tiår fra nå", t.format((315569259747L * 3L)));
+ }
+
+ @Test
+ public void testCenturiesFromNow() throws Exception {
+ PrettyTime t = new PrettyTime((0), locale);
+ assertEquals("3 århundre fra nå", t.format((3155692597470L * 3L)));
+ }
+
+ /*
+ * Past
+ */
+ @Test
+ public void testMomentsAgo() throws Exception {
+ PrettyTime t = new PrettyTime((6000), locale);
+ assertEquals("et øyeblikk siden", t.format((0)));
+ }
+
+ @Test
+ public void testMinutesAgo() throws Exception {
+ PrettyTime t = new PrettyTime((1000 * 60 * 12), locale);
+ assertEquals("12 minutter siden", t.format((0)));
+ }
+
+ @Test
+ public void testHoursAgo() throws Exception {
+ PrettyTime t = new PrettyTime((1000 * 60 * 60 * 3), locale);
+ assertEquals("3 timer siden", t.format((0)));
+ }
+
+ @Test
+ public void testDaysAgo() throws Exception {
+ PrettyTime t = new PrettyTime((1000 * 60 * 60 * 24 * 3), locale);
+ assertEquals("3 dager siden", t.format((0)));
+ }
+
+ @Test
+ public void testWeeksAgo() throws Exception {
+ PrettyTime t = new PrettyTime((1000 * 60 * 60 * 24 * 7 * 3), locale);
+ assertEquals("3 uker siden", t.format((0)));
+ }
+
+ @Test
+ public void testMonthsAgo() throws Exception {
+ PrettyTime t = new PrettyTime((2629743830L * 3L), locale);
+ assertEquals("3 måneder siden", t.format((0)));
+ }
+
+ @Test
+ public void testYearsAgo() throws Exception {
+ PrettyTime t = new PrettyTime((2629743830L * 12L * 3L), locale);
+ assertEquals("3 år siden", t.format((0)));
+ }
+
+ @Test
+ public void testDecadesAgo() throws Exception {
+ PrettyTime t = new PrettyTime((315569259747L * 3L), locale);
+ assertEquals("3 tiår siden", t.format((0)));
+ }
+
+ @Test
+ public void testCenturiesAgo() throws Exception {
+ PrettyTime t = new PrettyTime((3155692597470L * 3L), locale);
+ assertEquals("3 århundre siden", t.format((0)));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ Locale.setDefault(defaultLocale);
+ }
+}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_RU_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_RU_Test.java
index 5a912ac..aa595cc 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_RU_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_RU_Test.java
@@ -13,10 +13,13 @@ import static org.junit.Assert.assertEquals;
public class PrettyTimeI18n_RU_Test {
+ private Locale defaultLocale;
+
private Locale locale;
@Before
public void setUp() throws Exception {
+ defaultLocale = Locale.getDefault();
locale = new Locale("ru");
Locale.setDefault(locale);
}
@@ -163,9 +166,8 @@ public class PrettyTimeI18n_RU_Test {
assertEquals("3 века назад", t.format((0)));
}
-
@After
public void tearDown() throws Exception {
- Locale.setDefault(locale);
+ Locale.setDefault(defaultLocale);
}
}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_SV_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_SV_Test.java
index a833e4d..0a5d0a1 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_SV_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_SV_Test.java
@@ -1,5 +1,6 @@
package org.xbib.time.pretty;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -12,11 +13,15 @@ import static org.junit.Assert.assertEquals;
public class PrettyTimeI18n_SV_Test {
+ private Locale defaultLocale;
+
private Locale locale;
@Before
public void setUp() throws Exception {
+ defaultLocale = Locale.getDefault();
locale = new Locale("sv");
+ Locale.setDefault(locale);
}
@Test
@@ -161,4 +166,9 @@ public class PrettyTimeI18n_SV_Test {
PrettyTime t = new PrettyTime((3155692597470L * 3L), locale);
assertEquals("3 århundraden sedan", t.format((0)));
}
+
+ @After
+ public void tearDown() throws Exception {
+ Locale.setDefault(defaultLocale);
+ }
}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_Test.java
index b9bfe7b..6726925 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_Test.java
@@ -1,130 +1,128 @@
-package org.xbib.time.pretty;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.util.Locale;
-
-import static org.junit.Assert.assertEquals;
-
-public class PrettyTimeI18n_Test {
-
- // Stores current locale so that it can be restored
- private Locale locale;
-
- // Method setUp() is called automatically before every test method
- @Before
- public void setUp() throws Exception {
- locale = Locale.getDefault();
- }
-
- @Test
- public void testPrettyTimeDefault() {
- // The default resource bundle should be used
- PrettyTime p = new PrettyTime(0, Locale.ROOT);
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1), ZoneId.systemDefault());
- assertEquals("moments from now", p.format(localDateTime));
- }
-
- @Test
- public void testPrettyTimeGerman() {
- // The German resource bundle should be used
- PrettyTime p = new PrettyTime(Locale.GERMAN);
- LocalDateTime ref = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- p.setReference(ref);
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1), ZoneId.systemDefault());
- assertEquals("Jetzt", p.format(localDateTime));
- }
-
- @Test
- public void testPrettyTimeSpanish() {
- // The Spanish resource bundle should be used
- PrettyTime p = new PrettyTime(new Locale("es"));
- assertEquals("en un instante", p.format(LocalDateTime.now()));
- }
-
- @Test
- public void testPrettyTimeDefaultCenturies() {
- // The default resource bundle should be used
- PrettyTime p = new PrettyTime((3155692597470L * 3L), Locale.ROOT);
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- assertEquals("3 centuries ago", p.format(localDateTime));
- }
-
- @Test
- public void testPrettyTimeGermanCenturies() {
- // The default resource bundle should be used
- PrettyTime p = new PrettyTime((3155692597470L * 3L), Locale.GERMAN);
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- assertEquals("vor 3 Jahrhunderten", p.format(localDateTime));
- }
-
- @Test
- public void testPrettyTimeViaDefaultLocaleDefault() {
- // The default resource bundle should be used
- Locale.setDefault(Locale.ROOT);
- PrettyTime p = new PrettyTime((0));
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1), ZoneId.systemDefault());
- assertEquals("moments from now", p.format(localDateTime));
- }
-
- @Test
- public void testPrettyTimeViaDefaultLocaleGerman() {
- // The German resource bundle should be used
- Locale.setDefault(Locale.GERMAN);
- PrettyTime p = new PrettyTime((0));
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1), ZoneId.systemDefault());
- assertEquals("Jetzt", p.format(localDateTime));
- }
-
- @Test
- public void testPrettyTimeViaDefaultLocaleDefaultCenturies() {
- // The default resource bundle should be used
- Locale.setDefault(Locale.ROOT);
- PrettyTime p = new PrettyTime((3155692597470L * 3L));
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- assertEquals("3 centuries ago", p.format(localDateTime));
- }
-
- @Test
- public void testPrettyTimeViaDefaultLocaleGermanCenturies() {
- // The default resource bundle should be used
- Locale.setDefault(Locale.GERMAN);
- PrettyTime p = new PrettyTime((3155692597470L * 3L));
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- assertEquals("vor 3 Jahrhunderten", p.format(localDateTime));
- }
-
- @Test
- public void testPrettyTimeRootLocale() {
- long t = 1L;
- PrettyTime p = new PrettyTime(0, Locale.ROOT);
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- while (1000L * 60L * 60L * 24L * 365L * 1000000L > t) {
- assertEquals(p.format(localDateTime).endsWith("now"), true);
- t *= 2L;
- }
- }
-
- @Test
- public void testPrettyTimeGermanLocale() {
- long t = 1L;
- PrettyTime p = new PrettyTime(0, Locale.GERMAN);
- LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
- while (1000L * 60L * 60L * 24L * 365L * 1000000L > t) {
- assertEquals(p.format(localDateTime).startsWith("in") || p.format(localDateTime).startsWith("Jetzt"), true);
- t *= 2L;
- }
- }
-
- // Method tearDown() is called automatically after every test method
- @After
- public void tearDown() throws Exception {
- Locale.setDefault(locale);
- }
-
-}
+package org.xbib.time.pretty;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Locale;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class PrettyTimeI18n_Test {
+
+ private Locale locale;
+
+ @Before
+ public void setUp() throws Exception {
+ locale = Locale.getDefault();
+ }
+
+ @Test
+ public void testPrettyTimeDefault() {
+ // The default resource bundle should be used
+ PrettyTime p = new PrettyTime(0, Locale.ROOT);
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1), ZoneId.systemDefault());
+ assertEquals("moments from now", p.format(localDateTime));
+ }
+
+ @Test
+ public void testPrettyTimeGerman() {
+ // The German resource bundle should be used
+ PrettyTime p = new PrettyTime(Locale.GERMAN);
+ LocalDateTime ref = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ p.setReference(ref);
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1), ZoneId.systemDefault());
+ assertEquals("Jetzt", p.format(localDateTime));
+ }
+
+ @Test
+ public void testPrettyTimeSpanish() {
+ // The Spanish resource bundle should be used
+ PrettyTime p = new PrettyTime(new Locale("es"));
+ assertEquals("en un instante", p.format(LocalDateTime.now()));
+ }
+
+ @Test
+ public void testPrettyTimeDefaultCenturies() {
+ // The default resource bundle should be used
+ PrettyTime p = new PrettyTime((3155692597470L * 3L), Locale.ROOT);
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ assertEquals("3 centuries ago", p.format(localDateTime));
+ }
+
+ @Test
+ public void testPrettyTimeGermanCenturies() {
+ // The default resource bundle should be used
+ PrettyTime p = new PrettyTime((3155692597470L * 3L), Locale.GERMAN);
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ assertEquals("vor 3 Jahrhunderten", p.format(localDateTime));
+ }
+
+ @Test
+ public void testPrettyTimeViaDefaultLocaleDefault() {
+ // The default resource bundle should be used
+ Locale.setDefault(Locale.ROOT);
+ PrettyTime p = new PrettyTime((0));
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1), ZoneId.systemDefault());
+ assertEquals("moments from now", p.format(localDateTime));
+ }
+
+ @Test
+ public void testPrettyTimeViaDefaultLocaleGerman() {
+ // The German resource bundle should be used
+ Locale.setDefault(Locale.GERMAN);
+ PrettyTime p = new PrettyTime((0));
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(1), ZoneId.systemDefault());
+ assertEquals("Jetzt", p.format(localDateTime));
+ }
+
+ @Test
+ public void testPrettyTimeViaDefaultLocaleDefaultCenturies() {
+ // The default resource bundle should be used
+ Locale.setDefault(Locale.ROOT);
+ PrettyTime p = new PrettyTime((3155692597470L * 3L));
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ assertEquals("3 centuries ago", p.format(localDateTime));
+ }
+
+ @Test
+ public void testPrettyTimeViaDefaultLocaleGermanCenturies() {
+ // The default resource bundle should be used
+ Locale.setDefault(Locale.GERMAN);
+ PrettyTime p = new PrettyTime((3155692597470L * 3L));
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ assertEquals("vor 3 Jahrhunderten", p.format(localDateTime));
+ }
+
+ @Test
+ public void testPrettyTimeRootLocale() {
+ long t = 1L;
+ PrettyTime p = new PrettyTime(0, Locale.ROOT);
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ while (1000L * 60L * 60L * 24L * 365L * 1000000L > t) {
+ assertTrue(p.format(localDateTime).endsWith("now"));
+ t *= 2L;
+ }
+ }
+
+ @Test
+ public void testPrettyTimeGermanLocale() {
+ long t = 1L;
+ PrettyTime p = new PrettyTime(0, Locale.GERMAN);
+ LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.systemDefault());
+ while (1000L * 60L * 60L * 24L * 365L * 1000000L > t) {
+ assertTrue(p.format(localDateTime).startsWith("in") || p.format(localDateTime).startsWith("Jetzt"));
+ t *= 2L;
+ }
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ Locale.setDefault(locale);
+ }
+
+}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_UA_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_UA_Test.java
index babd14b..0292d6d 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_UA_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_UA_Test.java
@@ -13,10 +13,13 @@ import static org.junit.Assert.assertEquals;
public class PrettyTimeI18n_UA_Test {
+ private Locale defaultLocale;
+
private Locale locale;
@Before
public void setUp() throws Exception {
+ defaultLocale = Locale.getDefault();
locale = new Locale("ua");
Locale.setDefault(locale);
}
@@ -189,6 +192,6 @@ public class PrettyTimeI18n_UA_Test {
@After
public void tearDown() throws Exception {
- Locale.setDefault(locale);
+ Locale.setDefault(defaultLocale);
}
}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_hi_IN_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_hi_IN_Test.java
index 3629bd1..849bcad 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_hi_IN_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_hi_IN_Test.java
@@ -15,10 +15,13 @@ import static org.junit.Assert.assertTrue;
public class PrettyTimeI18n_hi_IN_Test {
+ private Locale defaultLocale;
+
private Locale locale;
@Before
public void setUp() throws Exception {
+ defaultLocale = Locale.getDefault();
locale = new Locale("hi", "IN");
Locale.setDefault(locale);
}
@@ -27,7 +30,7 @@ public class PrettyTimeI18n_hi_IN_Test {
public void testLocaleISOCorrectness() {
assertEquals("hi", this.locale.getLanguage());
assertEquals("IN", this.locale.getCountry());
- assertEquals("हिंदी", this.locale.getDisplayLanguage());
+ assertEquals("हिन्दी", this.locale.getDisplayLanguage());
assertEquals("भारत", this.locale.getDisplayCountry());
}
@@ -243,6 +246,6 @@ public class PrettyTimeI18n_hi_IN_Test {
@After
public void tearDown() throws Exception {
- Locale.setDefault(Locale.ENGLISH);
+ Locale.setDefault(defaultLocale);
}
}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_in_ID_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_in_ID_Test.java
index 0f13192..624f820 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_in_ID_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_in_ID_Test.java
@@ -15,10 +15,13 @@ import static org.junit.Assert.assertTrue;
public class PrettyTimeI18n_in_ID_Test {
+ private Locale defaultLocale;
+
private Locale locale;
@Before
public void setUp() throws Exception {
+ defaultLocale = Locale.getDefault();
locale = new Locale("in", "ID");
Locale.setDefault(locale);
}
@@ -242,6 +245,6 @@ public class PrettyTimeI18n_in_ID_Test {
@After
public void tearDown() throws Exception {
- Locale.setDefault(Locale.ENGLISH);
+ Locale.setDefault(defaultLocale);
}
}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_zh_TW_Test.java b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_zh_TW_Test.java
index dd9e14b..176c215 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_zh_TW_Test.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeI18n_zh_TW_Test.java
@@ -11,12 +11,15 @@ import static org.junit.Assert.assertEquals;
public class PrettyTimeI18n_zh_TW_Test {
+ private Locale defaultLocale;
+
private Locale locale;
@Before
public void setUp() throws Exception {
+ defaultLocale = Locale.getDefault();
locale = Locale.TRADITIONAL_CHINESE;
- Locale.setDefault(Locale.TRADITIONAL_CHINESE);
+ Locale.setDefault(locale);
}
@Test
@@ -185,6 +188,6 @@ public class PrettyTimeI18n_zh_TW_Test {
@After
public void tearDown() throws Exception {
- Locale.setDefault(locale);
+ Locale.setDefault(defaultLocale);
}
}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeLocaleFallbackTest.java b/src/test/java/org/xbib/time/pretty/PrettyTimeLocaleFallbackTest.java
index 752bc45..eb581e6 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeLocaleFallbackTest.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeLocaleFallbackTest.java
@@ -11,13 +11,11 @@ import static org.junit.Assert.assertEquals;
public class PrettyTimeLocaleFallbackTest {
- // Stores current locale so that it can be restored
- private Locale locale;
+ private Locale defaultLocale;
- // Method setUp() is called automatically before every test method
@Before
public void setUp() throws Exception {
- locale = Locale.getDefault();
+ defaultLocale = Locale.getDefault();
Locale.setDefault(new Locale("Foo", "Bar"));
}
@@ -32,7 +30,7 @@ public class PrettyTimeLocaleFallbackTest {
@After
public void tearDown() throws Exception {
- Locale.setDefault(locale);
+ Locale.setDefault(defaultLocale);
}
}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeTest.java b/src/test/java/org/xbib/time/pretty/PrettyTimeTest.java
index f30520c..65ca363 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeTest.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeTest.java
@@ -15,11 +15,11 @@ import static org.junit.Assert.assertTrue;
public class PrettyTimeTest {
- private Locale locale;
+ private Locale defaultLocale;
@Before
public void setUp() throws Exception {
- locale = Locale.getDefault();
+ defaultLocale = Locale.getDefault();
Locale.setDefault(Locale.ROOT);
}
@@ -236,7 +236,6 @@ public class PrettyTimeTest {
@After
public void tearDown() throws Exception {
- Locale.setDefault(locale);
+ Locale.setDefault(defaultLocale);
}
-
}
diff --git a/src/test/java/org/xbib/time/pretty/PrettyTimeUnitConfigurationTest.java b/src/test/java/org/xbib/time/pretty/PrettyTimeUnitConfigurationTest.java
index 75f3379..d11884d 100644
--- a/src/test/java/org/xbib/time/pretty/PrettyTimeUnitConfigurationTest.java
+++ b/src/test/java/org/xbib/time/pretty/PrettyTimeUnitConfigurationTest.java
@@ -17,11 +17,11 @@ import static org.junit.Assert.assertEquals;
public class PrettyTimeUnitConfigurationTest {
- private Locale locale;
+ private Locale defaultLocale;
@Before
public void setUp() throws Exception {
- locale = Locale.getDefault();
+ defaultLocale = Locale.getDefault();
Locale.setDefault(Locale.ROOT);
}
@@ -53,7 +53,6 @@ public class PrettyTimeUnitConfigurationTest {
@After
public void tearDown() throws Exception {
- Locale.setDefault(locale);
+ Locale.setDefault(defaultLocale);
}
-
}
diff --git a/src/test/java/org/xbib/time/pretty/SimpleTimeFormatTest.java b/src/test/java/org/xbib/time/pretty/SimpleTimeFormatTest.java
index 0d46260..4b1736a 100644
--- a/src/test/java/org/xbib/time/pretty/SimpleTimeFormatTest.java
+++ b/src/test/java/org/xbib/time/pretty/SimpleTimeFormatTest.java
@@ -13,11 +13,11 @@ import static org.junit.Assert.assertEquals;
public class SimpleTimeFormatTest {
- private Locale locale;
+ private Locale defaultLocale;
@Before
public void setUp() throws Exception {
- locale = Locale.getDefault();
+ defaultLocale = Locale.getDefault();
Locale.setDefault(Locale.ROOT);
}
@@ -46,7 +46,7 @@ public class SimpleTimeFormatTest {
@After
public void tearDown() throws Exception {
- Locale.setDefault(locale);
+ Locale.setDefault(defaultLocale);
}
}
diff --git a/src/test/java/org/xbib/time/pretty/i18n/SimpleTimeFormatTimeQuantifiedNameTest.java b/src/test/java/org/xbib/time/pretty/i18n/SimpleTimeFormatTimeQuantifiedNameTest.java
index 8ec02cc..f5f2467 100644
--- a/src/test/java/org/xbib/time/pretty/i18n/SimpleTimeFormatTimeQuantifiedNameTest.java
+++ b/src/test/java/org/xbib/time/pretty/i18n/SimpleTimeFormatTimeQuantifiedNameTest.java
@@ -12,12 +12,16 @@ import java.time.ZoneId;
import java.util.Locale;
public class SimpleTimeFormatTimeQuantifiedNameTest {
+
+ private Locale defaultLocale;
+
private Locale locale;
@Before
public void setUp() throws Exception {
- locale = Locale.getDefault();
- Locale.setDefault(new Locale("yy"));
+ defaultLocale = Locale.getDefault();
+ locale = new Locale("yy");
+ Locale.setDefault(locale);
}
@Test
@@ -76,10 +80,8 @@ public class SimpleTimeFormatTimeQuantifiedNameTest {
Assert.assertEquals("1 hour ago", p.format(0));
}
- // Method tearDown() is called automatically after every test method
@After
public void tearDown() throws Exception {
- Locale.setDefault(locale);
+ Locale.setDefault(defaultLocale);
}
-
}
diff --git a/src/test/java/org/xbib/time/pretty/i18n/TimeFormatProviderTest.java b/src/test/java/org/xbib/time/pretty/i18n/TimeFormatProviderTest.java
index 34e5f7f..a4c8c76 100644
--- a/src/test/java/org/xbib/time/pretty/i18n/TimeFormatProviderTest.java
+++ b/src/test/java/org/xbib/time/pretty/i18n/TimeFormatProviderTest.java
@@ -9,21 +9,26 @@ import java.util.Locale;
import java.util.ResourceBundle;
public class TimeFormatProviderTest {
+
@Test
public void test() {
+ Locale defaultLocale = Locale.getDefault();
Locale locale = new Locale("xx");
Locale.setDefault(locale);
ResourceBundle bundle = ResourceBundle.getBundle(Resources.class.getName(), locale);
Assert.assertTrue(bundle instanceof TimeFormatProvider);
+ Locale.setDefault(defaultLocale);
}
@Test
public void testFormatFromDirectFormatOverride() throws Exception {
+ Locale defaultLocale = Locale.getDefault();
Locale locale = new Locale("xx");
Locale.setDefault(locale);
PrettyTime prettyTime = new PrettyTime(locale);
String result = prettyTime.format(System.currentTimeMillis() + 1000 * 60 * 6);
Assert.assertEquals("6 minutes from now", result);
+ Locale.setDefault(defaultLocale);
}
}
diff --git a/src/test/java/org/xbib/time/schedule/CompareBehaviorToQuartzTest.java b/src/test/java/org/xbib/time/schedule/CompareBehaviorToQuartzTest.java
new file mode 100644
index 0000000..e295cbc
--- /dev/null
+++ b/src/test/java/org/xbib/time/schedule/CompareBehaviorToQuartzTest.java
@@ -0,0 +1,261 @@
+package org.xbib.time.schedule;
+
+import static org.junit.Assert.assertTrue;
+import static org.xbib.time.schedule.DateTimes.toDates;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.DayOfWeek;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalAdjusters;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class CompareBehaviorToQuartzTest {
+
+ static final CronExpression.Parser quartzLike = CronExpression.parser()
+ .withSecondsField(true)
+ .withOneBasedDayOfWeek(true)
+ .allowBothDayFields(false);
+ private static final String timeFormatString = "s m H d M E yyyy";
+ private static final DateTimeFormatter dateTimeFormat = DateTimeFormatter.ofPattern(timeFormatString);
+ private final DateFormat dateFormat = new SimpleDateFormat(timeFormatString);
+
+ protected String string;
+ private Times expected;
+
+ @Before
+ public void before() {
+ expected = new Times();
+ }
+
+ @Test
+ public void complex() throws Exception {
+ string = "0/5 14,18,3-39,52 * ? JAN,MAR,SEP MON-FRI 2002-2010";
+ expected.seconds.with(0);
+ expected.minutes.with(52).withRange(3, 39);
+ expected.months.with(1, 3, 9);
+ expected.daysOfWeek.withRange(1, 5);
+ expected.years.withRange(2002, 2010);
+ check();
+ }
+
+ @Test
+ public void at_noon_every_day() throws Exception {
+ string = "0 0 12 * * ?";
+ expected.seconds.with(0);
+ expected.minutes.with(0);
+ expected.hours.with(12);
+ check();
+ }
+
+ @Test
+ public void at_10_15am_every_day1() throws Exception {
+ string = "0 15 10 ? * *";
+ expected.seconds.with(0);
+ expected.minutes.with(15);
+ expected.hours.with(10);
+ check();
+ }
+
+ @Test
+ public void at_10_15am_every_day2() throws Exception {
+ string = "0 15 10 * * ?";
+ expected.seconds.with(0);
+ expected.minutes.with(15);
+ expected.hours.with(10);
+ check();
+ }
+
+ @Test
+ public void at_10_15am_every_day3() throws Exception {
+ string = "0 15 10 * * ? *";
+ expected.seconds.with(0);
+ expected.minutes.with(15);
+ expected.hours.with(10);
+ check();
+ }
+
+
+ @Test
+ public void at_10_15am_every_day_in_2005() throws Exception {
+ string = "0 15 10 * * ? 2005";
+ expected.seconds.with(0);
+ expected.minutes.with(15);
+ expected.hours.with(10);
+ expected.years.with(2005);
+ check();
+ }
+
+ @Test
+ public void every_minute_of_2pm() throws Exception {
+ string = "0 * 14 * * ?";
+ expected.seconds.with(0);
+ expected.hours.with(14);
+ check();
+ }
+
+ @Test
+ public void every_5_minutes_of_2pm() throws Exception {
+ string = "0 0/5 14 * * ?";
+ expected.seconds.with(0);
+ expected.minutes.withRange(0, 59, 5);
+ expected.hours.with(14);
+ check();
+ }
+
+ @Test
+ public void every_5_minutes_of_2pm_and_6pm() throws Exception {
+ string = "0 0/5 14,18 * * ?";
+ expected.seconds.with(0);
+ expected.minutes.withRange(0, 59, 5);
+ expected.hours.with(14, 18);
+ check();
+ }
+
+ @Test
+ public void first_5_minutes_of_2pm() throws Exception {
+ string = "0 0-5 14 * * ?";
+ expected.seconds.with(0);
+ expected.minutes.withRange(0, 5);
+ expected.hours.with(14);
+ check();
+ }
+
+ @Test
+ public void at_2_10pm_and_2_44pm_every_wednesday_in_march() throws Exception {
+ string = "0 10,44 14 ? 3 WED";
+ expected.seconds.with(0);
+ expected.minutes.with(10, 44);
+ expected.hours.with(14);
+ expected.months.with(3);
+ expected.daysOfWeek.with(3);
+ check();
+ }
+
+ @Test
+ public void at_10_15am_every_weekday() throws Exception {
+ string = "0 15 10 ? * MON-FRI";
+ expected.seconds.with(0);
+ expected.minutes.with(15);
+ expected.hours.with(10);
+ expected.daysOfWeek.withRange(1, 5);
+ check();
+ }
+
+ @Test
+ public void at_10_15am_on_the_15th_of_every_month() throws Exception {
+ string = "0 15 10 15 * ?";
+ expected.seconds.with(0);
+ expected.minutes.with(15);
+ expected.hours.with(10);
+ expected.daysOfMonth.with(15);
+ check();
+ }
+
+ @Test
+ public void at_10_15am_on_the_last_day_of_every_month() throws Exception {
+ string = "0 15 10 L * ?";
+ List times = new ArrayList<>();
+ ZonedDateTime t = ZonedDateTime.now().withDayOfYear(1).truncatedTo(ChronoUnit.DAYS).plusHours(10).plusMinutes(15);
+ int year = t.getYear();
+ while (t.getYear() == year) {
+ times.add(t.with(TemporalAdjusters.lastDayOfMonth()));
+ t = t.plusMonths(1);
+ }
+ check(times);
+ }
+
+ @Test
+ public void at_10_15am_on_the_last_friday_of_every_month() throws Exception {
+ string = "0 15 10 ? * 6L";
+ List times = new ArrayList<>();
+ ZonedDateTime t = ZonedDateTime.now().truncatedTo(ChronoUnit.DAYS).plusHours(10).plusMinutes(15);
+ int year = t.getYear();
+ while (t.getYear() == year) {
+ times.add(DateTimes.lastOfMonth(t, DayOfWeek.FRIDAY));
+ t = t.plusMonths(1);
+ }
+ check(times);
+ }
+
+ @Test
+ public void at_10_15am_on_the_last_friday_of_every_month_during_2002_through_2005() throws Exception {
+ string = "0 15 10 ? * 6L 2002-2005";
+ List times = new ArrayList<>();
+ for (int year = 2002; year <= 2005; year++) {
+ ZonedDateTime t = ZonedDateTime.now().withYear(year).truncatedTo(ChronoUnit.DAYS).plusHours(10).plusMinutes(15);
+ while (t.getYear() == year) {
+ times.add(DateTimes.lastOfMonth(t, DayOfWeek.FRIDAY));
+ t = t.plusMonths(1);
+ }
+ }
+ check(times);
+ }
+
+
+ @Test
+ @Ignore
+ // TODO let's see if we can make this more reliably faster than the respective quartz run
+ public void at_10_15am_on_the_third_friday_of_every_month() throws Exception {
+ string = "0 15 10 ? * 6#3";
+ List times = new ArrayList<>();
+ ZonedDateTime t = ZonedDateTime.now().withDayOfYear(1).truncatedTo(ChronoUnit.DAYS).plusHours(10).plusMinutes(15);
+ int year = t.getYear();
+ while (t.getYear() == year) {
+ times.add(DateTimes.nthOfMonth(t, DayOfWeek.FRIDAY, 3));
+ t = t.plusMonths(1);
+ }
+ check(times);
+ }
+
+ @Test
+ public void at_noon_every_5_days_every_month_starting_on_the_first_day_of_the_month() throws Exception {
+ string = "0 0 12 1/5 * ?";
+ expected.seconds.with(0);
+ expected.minutes.with(0);
+ expected.hours.with(12);
+ expected.daysOfMonth.withRange(1, 31, 5);
+ check();
+ }
+
+ @Test
+ public void november_11th_at_11_11am() throws Exception {
+ string = "0 11 11 11 11 ?";
+ expected.seconds.with(0);
+ expected.minutes.with(11);
+ expected.hours.with(11);
+ expected.daysOfMonth.with(11);
+ expected.months.with(11);
+ check();
+ }
+
+ private void check() throws ParseException {
+ check(expected.dateTimes());
+ }
+
+ protected void check(Iterable times) throws ParseException {
+ checkLocalImplementation(times);
+ checkQuartzImplementation(toDates(times));
+ }
+
+ private void checkQuartzImplementation(Iterable times) throws ParseException {
+ org.quartz.CronExpression quartz = new org.quartz.CronExpression(string);
+ for (Date time : times) {
+ assertTrue(dateFormat.format(time).toUpperCase() + " doesn't match expression: " + string, quartz.isSatisfiedBy(time));
+ }
+ }
+
+ private void checkLocalImplementation(Iterable times) {
+ CronExpression expr = quartzLike.parse(string);
+ for (ZonedDateTime time : times) {
+ assertTrue(time.format(dateTimeFormat).toUpperCase() + " doesn't match expression: " + string, expr.matches(time));
+ }
+ }
+}
diff --git a/src/test/java/org/xbib/time/schedule/CompareSizeToQuartzTest.java b/src/test/java/org/xbib/time/schedule/CompareSizeToQuartzTest.java
new file mode 100644
index 0000000..9e63ab3
--- /dev/null
+++ b/src/test/java/org/xbib/time/schedule/CompareSizeToQuartzTest.java
@@ -0,0 +1,123 @@
+package org.xbib.time.schedule;
+
+import static org.junit.Assert.assertTrue;
+import com.google.caliper.memory.ObjectGraphMeasurer;
+import org.junit.Test;
+
+public class CompareSizeToQuartzTest {
+
+ private static final CronExpression.Parser quartzLike = CronExpression.parser()
+ .withSecondsField(true)
+ .withOneBasedDayOfWeek(true)
+ .allowBothDayFields(false);
+
+ @Test
+ public void complex() throws Exception {
+ check("0/5 14,18,3-39,52 * ? JAN,MAR,SEP MON-FRI 2002-2010");
+ }
+
+ @Test
+ public void at_noon_every_day() throws Exception {
+ check("0 0 12 * * ?");
+ }
+
+ @Test
+ public void at_10_15am_every_day1() throws Exception {
+ check("0 15 10 ? * *");
+ }
+
+ @Test
+ public void at_10_15am_every_day2() throws Exception {
+ check("0 15 10 * * ?");
+ }
+
+ @Test
+ public void at_10_15am_every_day3() throws Exception {
+ check("0 15 10 * * ? *");
+ }
+
+ @Test
+ public void at_10_15am_every_day_in_2005() throws Exception {
+ check("0 15 10 * * ? 2005");
+ }
+
+ @Test
+ public void every_minute_of_2pm() throws Exception {
+ check("0 * 14 * * ?");
+ }
+
+ @Test
+ public void every_5_minutes_of_2pm() throws Exception {
+ check("0 0/5 14 * * ?");
+ }
+
+ @Test
+ public void every_5_minutes_of_2pm_and_6pm() throws Exception {
+ check("0 0/5 14,18 * * ?");
+ }
+
+ @Test
+ public void first_5_minutes_of_2pm() throws Exception {
+ check("0 0-5 14 * * ?");
+ }
+
+ @Test
+ public void at_2_10pm_and_2_44pm_every_wednesday_in_march() throws Exception {
+ check("0 10,44 14 ? 3 WED");
+ }
+
+ @Test
+ public void at_10_15am_every_weekday() throws Exception {
+ check("0 15 10 ? * MON-FRI");
+ }
+
+ @Test
+ public void at_10_15am_on_the_15th_of_every_month() throws Exception {
+ check("0 15 10 15 * ?");
+ }
+
+ @Test
+ public void at_10_15am_on_the_last_day_of_every_month() throws Exception {
+ check("0 15 10 L * ?");
+ }
+
+ @Test
+ public void at_10_15am_on_the_last_friday_of_every_month() throws Exception {
+ check("0 15 10 ? * 6L");
+ }
+
+ @Test
+ public void at_10_15am_on_the_last_friday_of_every_month_during_2002_through_2005() throws Exception {
+ check("0 15 10 ? * 6L 2002-2005");
+ }
+
+ @Test
+ public void at_10_15am_on_the_third_friday_of_every_month() throws Exception {
+ check("0 15 10 ? * 6#3");
+ }
+
+ @Test
+ public void at_noon_every_5_days_every_month_starting_on_the_first_day_of_the_month() throws Exception {
+ check("0 0 12 1/5 * ?");
+ }
+
+ @Test
+ public void november_11th_at_11_11am() throws Exception {
+ check("0 11 11 11 11 ?");
+ }
+
+ private void check(String expression) throws Exception {
+ CronExpression local = quartzLike.parse(expression);
+ org.quartz.CronExpression quartz = new org.quartz.CronExpression(expression);
+ long localSize = ObjectSizeCalculator.getObjectSize(local);
+ long quartzSize = ObjectSizeCalculator.getObjectSize(quartz);
+ assertTrue("We have more bytes", localSize < quartzSize);
+ ObjectGraphMeasurer.Footprint localFoot = ObjectGraphMeasurer.measure(local);
+ ObjectGraphMeasurer.Footprint quartzFoot = ObjectGraphMeasurer.measure(quartz);
+ assertTrue("We have more references", localFoot.getAllReferences() < quartzFoot.getAllReferences());
+ assertTrue("We have more non-null references", localFoot.getNonNullReferences() < quartzFoot.getNonNullReferences());
+ //assertTrue("We have more null references", localFoot.getNullReferences() < quartzFoot.getNullReferences());
+ assertTrue("We have more objects", localFoot.getObjects() < quartzFoot.getObjects());
+ assertTrue("We have more primitives", localFoot.getPrimitives().size() < quartzFoot.getPrimitives().size());
+ }
+}
diff --git a/src/test/java/org/xbib/time/schedule/CompareSpeedToQuartzTest.java b/src/test/java/org/xbib/time/schedule/CompareSpeedToQuartzTest.java
new file mode 100644
index 0000000..abc5a94
--- /dev/null
+++ b/src/test/java/org/xbib/time/schedule/CompareSpeedToQuartzTest.java
@@ -0,0 +1,66 @@
+package org.xbib.time.schedule;
+
+import static org.junit.Assert.assertTrue;
+import com.google.common.base.Stopwatch;
+import java.text.ParseException;
+import java.time.ZonedDateTime;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.concurrent.TimeUnit;
+
+public class CompareSpeedToQuartzTest extends CompareBehaviorToQuartzTest {
+ @Override
+ protected void check(final Iterable times) throws ParseException {
+ final Iterable dates = DateTimes.toDates(times);
+ final CronExpression local = quartzLike.parse(string);
+ final org.quartz.CronExpression quartz = new org.quartz.CronExpression(string);
+ final int trials = 25;
+ final Stopwatch clock = Stopwatch.createStarted();
+ for (int i = 0; i < trials; i++) {
+ for (ZonedDateTime time : times) {
+ local.matches(time);
+ }
+ }
+ final long localNano = clock.elapsed(TimeUnit.NANOSECONDS);
+ clock.reset().start();
+ for (int i = 0; i < trials; i++) {
+ for (Date date : dates) {
+ quartz.isSatisfiedBy(date);
+ }
+ }
+ final long quartzNano = clock.elapsed(TimeUnit.NANOSECONDS);
+ final boolean lessThanOrEqual = localNano <= quartzNano;
+ System.out.printf(
+ "%-80s %-60s local %8.2fms %6s Quartz %8.2fms\n",
+ nameOfTestMethod(),
+ string,
+ localNano / 1000000d,
+ (lessThanOrEqual ? "<=" : ">"),
+ quartzNano / 1000000d
+ );
+ assertTrue(
+ "We took longer for expression '" + string + "'; " + localNano + " > " + quartzNano,
+ lessThanOrEqual
+ );
+ }
+
+ private String nameOfTestMethod() {
+ try {
+ throw new Exception();
+ } catch (Exception e) {
+ String method = null;
+ Iterator trace = Arrays.asList(e.getStackTrace()).iterator();
+ StackTraceElement element = trace.next();
+ while (getClass().getName().equals(element.getClassName())) {
+ element = trace.next();
+ }
+ String parentClassName = getClass().getSuperclass().getName();
+ while (element.getClassName().equals(parentClassName)) {
+ method = element.getMethodName();
+ element = trace.next();
+ }
+ return method;
+ }
+ }
+}
diff --git a/src/test/java/org/xbib/time/schedule/CronExpressionTest.java b/src/test/java/org/xbib/time/schedule/CronExpressionTest.java
new file mode 100644
index 0000000..a05c1e2
--- /dev/null
+++ b/src/test/java/org/xbib/time/schedule/CronExpressionTest.java
@@ -0,0 +1,344 @@
+package org.xbib.time.schedule;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.xbib.time.schedule.DateTimes.midnight;
+import static org.xbib.time.schedule.DateTimes.nearestWeekday;
+import static org.xbib.time.schedule.DateTimes.now;
+import static org.xbib.time.schedule.DateTimes.nthOfMonth;
+import static org.xbib.time.schedule.DateTimes.startOfHour;
+import org.junit.Test;
+import java.time.DayOfWeek;
+import java.time.Month;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.List;
+
+public class CronExpressionTest {
+
+ private static final CronExpression.Parser withSecondsField = CronExpression.parser().withSecondsField(true);
+
+ private CronExpression expression;
+
+ @Test
+ public void testHashCode() {
+ assertEquals(CronExpression.daily().hashCode(), CronExpression.parse("@daily").hashCode());
+ assertEquals(CronExpression.daily().hashCode(), CronExpression.parse("@midnight").hashCode());
+ assertEquals(CronExpression.hourly().hashCode(), CronExpression.parse("@hourly").hashCode());
+ assertEquals(CronExpression.monthly().hashCode(), CronExpression.parse("@monthly").hashCode());
+ assertEquals(CronExpression.weekly().hashCode(), CronExpression.parse("@weekly").hashCode());
+ assertEquals(CronExpression.yearly().hashCode(), CronExpression.parse("@annually").hashCode());
+ assertEquals(CronExpression.yearly().hashCode(), CronExpression.parse("@yearly").hashCode());
+ assertEquals(
+ CronExpression.parse("0 0 ? * 5#3,2#2").hashCode(),
+ CronExpression.parse("0 0 ? * 5#3,2#2").hashCode());
+ assertEquals(
+ withSecondsField.parse("0/5 14,18,3-39,52 * ? JAN,MAR,SEP MON-FRI 2002-2010").hashCode(),
+ withSecondsField.parse("0/5 14,18,3-39,52 * ? JAN,MAR,SEP MON-FRI 2002-2010").hashCode());
+ }
+
+ @Test
+ public void testEquals() {
+ assertEquals(CronExpression.daily(), CronExpression.parse("@daily"));
+ assertEquals(CronExpression.daily(), CronExpression.parse("@midnight"));
+ assertEquals(CronExpression.hourly(), CronExpression.parse("@hourly"));
+ assertEquals(CronExpression.monthly(), CronExpression.parse("@monthly"));
+ assertEquals(CronExpression.weekly(), CronExpression.parse("@weekly"));
+ assertEquals(CronExpression.yearly(), CronExpression.parse("@annually"));
+ assertEquals(CronExpression.yearly(), CronExpression.parse("@yearly"));
+ assertEquals(
+ CronExpression.parse("0 0 ? * 5#3,2#2"),
+ CronExpression.parse("0 0 ? * 5#3,2#2"));
+ assertEquals(
+ withSecondsField.parse("0/5 14,18,3-39,52 * ? JAN,MAR,SEP MON-FRI 2002-2010"),
+ withSecondsField.parse("0/5 14,18,3-39,52 * ? JAN,MAR,SEP MON-FRI 2002-2010"));
+ }
+
+ @Test
+ public void illegalCharacter() {
+ try {
+ expression = CronExpression.parse("0 0 4X * *");
+ fail("Expected exception");
+ } catch (IllegalArgumentException e) {
+ assertEquals("Bad character 'X' at position 5 in string: 0 0 4X * *", e.getMessage());
+ }
+ }
+
+ @Test
+ public void disallowBothDayFields() {
+ try {
+ expression = CronExpression.parser().allowBothDayFields(false).parse("0 0 1 * 5L");
+ fail("Expected exception");
+ } catch (IllegalArgumentException e) {
+ assertEquals("Day of month and day of week may not both be specified", e.getMessage());
+ }
+ }
+
+ @Test
+ public void nearestWeekdayWithoutNumber() {
+ try {
+ expression = CronExpression.parse("0 0 W * *");
+ } catch (IllegalArgumentException e) {
+ assertEquals("Bad character 'W' in day of month field: W", e.getMessage());
+ }
+ }
+
+ @Test
+ public void nearestWeekdayOfMonth() {
+ expression = CronExpression.parse("0 0 5W * *");
+ List times = new ArrayList<>();
+ ZonedDateTime t = DateTimes.startOfYear();
+ int year = t.getYear();
+ do {
+ times.add(nearestWeekday(t.withDayOfMonth(5)));
+ t = t.plusMonths(1);
+ } while (year == t.getYear());
+ assertMatchesAll(times);
+ }
+
+ @Test
+ public void nearestFriday() {
+ ZonedDateTime t = now().truncatedTo(ChronoUnit.DAYS).with(DayOfWeek.SATURDAY);
+ expression = CronExpression.parse("0 0 " + t.getDayOfMonth() + "W * *");
+ assertMatches(t.minusDays(1));
+ }
+
+ @Test
+ public void nearestMonday() {
+ ZonedDateTime t = now().truncatedTo(ChronoUnit.DAYS).with(DayOfWeek.SUNDAY);
+ expression = CronExpression.parse("0 0 " + t.getDayOfMonth() + "W * *");
+ assertMatches(t.plusDays(1));
+ }
+
+ @Test
+ public void nonMatchingNth() {
+ expression = CronExpression.parse("0 0 ? * 2#2");
+ List times = new ArrayList<>();
+ ZonedDateTime t = ZonedDateTime.now().truncatedTo(ChronoUnit.DAYS).withDayOfYear(1);
+ int year = t.getYear();
+ while (t.getYear() == year) {
+ times.add(nthOfMonth(t, DayOfWeek.TUESDAY, 1));
+ t = t.plusMonths(1);
+ }
+ for (ZonedDateTime time : times) {
+ assertFalse(expression.matches(time));
+ }
+ }
+
+ @Test
+ public void multipleNth() {
+ expression = CronExpression.parse("0 0 ? * 5#3,2#2");
+ List times = new ArrayList<>();
+ ZonedDateTime t = ZonedDateTime.now().truncatedTo(ChronoUnit.DAYS).withDayOfYear(1);
+ int year = t.getYear();
+ while (t.getYear() == year) {
+ times.add(nthOfMonth(t, DayOfWeek.FRIDAY, 3));
+ times.add(nthOfMonth(t, DayOfWeek.TUESDAY, 2));
+ t = t.plusMonths(1);
+ }
+ assertMatchesAll(times);
+ }
+
+ @Test
+ public void thirdFriday() {
+ String string = "0 0 ? * 5#3";
+ List times = new ArrayList<>();
+ ZonedDateTime t = ZonedDateTime.now().truncatedTo(ChronoUnit.DAYS).withDayOfYear(1);
+ int year = t.getYear();
+ while (t.getYear() == year) {
+ times.add(nthOfMonth(t, DayOfWeek.FRIDAY, 3));
+ t = t.plusMonths(1);
+ }
+ expression = CronExpression.parse(string);
+ assertMatchesAll(times);
+ }
+
+ @Test
+ public void reboot() {
+ expression = CronExpression.parse("@reboot");
+ ZonedDateTime now = now();
+ assertTrue(expression.matches(now));
+ assertFalse(expression.matches(now));
+ }
+
+ @Test
+ public void minuteFullRangeExplicit() {
+ expression = CronExpression.parse("0-59 * * * * *");
+ ZonedDateTime time = startOfHour();
+ int hour = time.getHour();
+ do {
+ assertMatches(time);
+ time = time.plusMinutes(1);
+ } while (time.getHour() == hour);
+ }
+
+ @Test
+ public void minuteRestrictedRange() {
+ expression = CronExpression.parse("10-20 * * * * *");
+ int first = 10, last = 20;
+ ZonedDateTime time = startOfHour();
+ int hour = time.getHour();
+ do {
+ int minute = time.getMinute();
+ assertEquals(first <= minute && minute <= last, expression.matches(time));
+ time = time.plusMinutes(1);
+ } while (time.getHour() == hour);
+ }
+
+ @Test
+ public void minuteFullRangeMod() {
+ expression = CronExpression.parse("*/5 * * * * *");
+ ZonedDateTime time = startOfHour();
+ int hour = time.getHour();
+ do {
+ int minute = time.getMinute();
+ assertEquals(minute % 5 == 0, expression.matches(time));
+ time = time.plusMinutes(1);
+ } while (time.getHour() == hour);
+ }
+
+ @Test
+ public void minuteRestrictedRangeMod() {
+ expression = CronExpression.parse("10-20/5 * * * * *");
+ int first = 10, last = 20;
+ ZonedDateTime time = startOfHour();
+ int hour = time.getHour();
+ do {
+ int minute = time.getMinute();
+ assertEquals(first <= minute && minute <= last && minute % 5 == 0, expression.matches(time));
+ time = time.plusMinutes(1);
+ } while (time.getHour() == hour);
+ }
+
+ @Test
+ public void yearly() {
+ expression = CronExpression.yearly();
+ assertYearly();
+ }
+
+ @Test
+ public void monthly() {
+ expression = CronExpression.monthly();
+ assertMonthly();
+ }
+
+ @Test
+ public void weekly() {
+ expression = CronExpression.weekly();
+ assertWeekly();
+ }
+
+ @Test
+ public void daily() {
+ expression = CronExpression.daily();
+ assertDaily();
+ }
+
+ @Test
+ public void hourly() {
+ expression = CronExpression.hourly();
+ assertHourly();
+ }
+
+ @Test
+ public void yearlyKeyword() {
+ expression = CronExpression.parse("@yearly");
+ assertYearly();
+ }
+
+ @Test
+ public void annualKeyword() {
+ expression = CronExpression.parse("@annually");
+ assertYearly();
+ }
+
+ @Test
+ public void monthlyKeyword() {
+ expression = CronExpression.parse("@monthly");
+ assertMonthly();
+ }
+
+ @Test
+ public void weeklyKeyword() {
+ expression = CronExpression.parse("@weekly");
+ assertWeekly();
+ }
+
+ @Test
+ public void dailyKeyword() {
+ expression = CronExpression.parse("@daily");
+ assertDaily();
+ }
+
+ @Test
+ public void hourlyKeyword() {
+ expression = CronExpression.parse("@hourly");
+ assertHourly();
+ }
+
+ @Test
+ public void invalid() {
+ assertFalse(CronExpression.isValid(null));
+ assertFalse(CronExpression.isValid(""));
+ assertFalse(CronExpression.isValid("a"));
+ assertFalse(CronExpression.isValid("0 0 1 * X"));
+ assertFalse(CronExpression.isValid("0 0 1 * 1X"));
+ }
+
+ @Test
+ public void invalidDueToSecondsField() {
+ assertTrue(CronExpression.isValid("0 0 1 * 1"));
+ assertFalse(CronExpression.parser().allowBothDayFields(false).isValid("0 0 1 * 1"));
+ }
+
+ private void assertWeekly() {
+ for (int week = 1; week <= 52; week++) {
+ assertMatches(midnight().withDayOfYear(7 * week).with(DayOfWeek.SUNDAY));
+ }
+ }
+
+ private void assertDaily() {
+ for (int day = 1; day <= 365; day++) {
+ assertMatches(midnight().withDayOfYear(day));
+ }
+ }
+
+ private void assertMonthly() {
+ for (Month month : Month.values()) {
+ assertMatches(midnight().with(month).withDayOfMonth(1));
+ }
+ }
+
+ private void assertHourly() {
+ for (int day = 1; day <= 365; day++) {
+ for (int hour = 0; hour <= 23; hour++) {
+ assertMatches(midnight().withDayOfYear(day).withHour(hour));
+ }
+ }
+ }
+
+ private void assertYearly() {
+ assertMatches(midnight().withDayOfYear(1));
+ }
+
+ private static final String formatString = "m H d M E yyyy";
+
+ private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(formatString);
+
+ private void assertMatchesAll(List times) {
+ for (ZonedDateTime time : times) {
+ assertMatches(time);
+ }
+ }
+
+ private void assertMatches(ZonedDateTime time) {
+ assertTrue(
+ time.format(formatter).toUpperCase() + " doesn't match expression: " + expression,
+ expression.matches(time)
+ );
+ }
+}
diff --git a/src/test/java/org/xbib/time/schedule/CronScheduleTest.java b/src/test/java/org/xbib/time/schedule/CronScheduleTest.java
new file mode 100644
index 0000000..82ecfcc
--- /dev/null
+++ b/src/test/java/org/xbib/time/schedule/CronScheduleTest.java
@@ -0,0 +1,126 @@
+package org.xbib.time.schedule;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.Multiset;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import java.io.IOException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class CronScheduleTest {
+
+ private static final Logger logger = Logger.getLogger(CronScheduleTest.class.getName());
+
+ private CronSchedule schedule;
+
+ private ScheduledExecutorService executor;
+
+ @Before
+ public void before() {
+ executor = Executors.newScheduledThreadPool(1);
+ }
+
+ @Test
+ public void runMinutes() throws InterruptedException {
+ schedule = new CronSchedule<>(executor, 60000);
+ final AtomicBoolean run = new AtomicBoolean(false);
+ schedule.add("test", CronExpression.parser()
+ .parse("* * * * * *"),
+ () -> {
+ run.set(true);
+ return null;
+ });
+ assertFalse(run.get());
+ schedule.start();
+ Thread.sleep(TimeUnit.MINUTES.toMillis(2));
+ assertTrue(run.get());
+ logger.log(Level.INFO, schedule.toString());
+ }
+
+ @Test
+ public void runSeconds() throws Exception {
+ schedule = new CronSchedule<>(executor, 1000);
+ final AtomicBoolean run = new AtomicBoolean(false);
+ schedule.add("test", CronExpression.parser()
+ .withSecondsField(true).parse("* * * * * *"),
+ () -> {
+ run.set(true);
+ return null;
+ });
+ assertFalse(run.get());
+ schedule.start();
+ Thread.sleep(TimeUnit.SECONDS.toMillis(2));
+ assertTrue(run.get());
+ }
+
+ @Test
+ public void removeOne() throws Exception {
+ schedule = new CronSchedule<>(executor, 1000);
+ final Multiset counts = HashMultiset.create();
+ Callable a = () -> {
+ counts.add("a");
+ return null;
+ };
+ Callable b = () -> {
+ counts.add("b");
+ return null;
+ };
+ CronExpression expression = CronExpression.parse("* * * * *");
+ schedule.add("1", expression, a);
+ schedule.add("2", expression, b);
+ runAndWait();
+ assertEquals(1, counts.count("a"));
+ assertEquals(1, counts.count("b"));
+ schedule.remove("1");
+ runAndWait();
+ assertEquals(1, counts.count("a"));
+ assertEquals(2, counts.count("b"));
+ }
+
+ @Test
+ public void removeAllForExpression() throws Exception {
+ schedule = new CronSchedule<>(executor, 1000);
+ final Multiset counts = HashMultiset.create();
+ Callable a = () -> {
+ counts.add("a");
+ return null;
+ };
+ Callable b = () -> {
+ counts.add("b");
+ return null;
+ };
+ CronExpression expression = CronExpression.parse("* * * * *");
+ schedule.add("a", expression, a);
+ schedule.add("b", expression, b);
+ runAndWait();
+ assertEquals(1, counts.count("a"));
+ assertEquals(1, counts.count("b"));
+ schedule.remove("a");
+ schedule.remove("b");
+ runAndWait();
+ assertEquals(1, counts.count("a"));
+ assertEquals(1, counts.count("b"));
+ }
+
+ @After
+ public void after() throws IOException {
+ if (schedule != null) {
+ schedule.close();
+ }
+ }
+
+ private void runAndWait() throws InterruptedException {
+ schedule.run();
+ Thread.sleep(10);
+ }
+}
diff --git a/src/test/java/org/xbib/time/schedule/DateTimes.java b/src/test/java/org/xbib/time/schedule/DateTimes.java
new file mode 100644
index 0000000..3c52c05
--- /dev/null
+++ b/src/test/java/org/xbib/time/schedule/DateTimes.java
@@ -0,0 +1,65 @@
+package org.xbib.time.schedule;
+
+import java.time.DayOfWeek;
+import java.time.Month;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalAdjusters;
+import java.util.Date;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+public class DateTimes {
+
+ static Iterable toDates(Iterable times) {
+ return StreamSupport.stream(times.spliterator(), false)
+ .map(input -> Date.from(input.toInstant())).collect(Collectors.toList());
+ }
+
+ static ZonedDateTime midnight() {
+ return now().truncatedTo(ChronoUnit.DAYS);
+ }
+
+ static ZonedDateTime startOfHour() {
+ return now().truncatedTo(ChronoUnit.HOURS);
+ }
+
+ public static ZonedDateTime now() {
+ return ZonedDateTime.now();
+ }
+
+ static ZonedDateTime lastOfMonth(ZonedDateTime t, DayOfWeek dayOfWeek) {
+ ZonedDateTime day = t.with(TemporalAdjusters.lastDayOfMonth()).with(dayOfWeek);
+ if (day.getMonth() != t.getMonth()) {
+ day = day.minusWeeks(1);
+ }
+ return day;
+ }
+
+ static ZonedDateTime nthOfMonth(ZonedDateTime t, DayOfWeek dayOfWeek, int desiredNumber) {
+ Month month = t.getMonth();
+ t = t.withDayOfMonth(1).with(dayOfWeek);
+ if (t.getMonth() != month) {
+ t = t.plusWeeks(1);
+ }
+ int number = 1;
+ while (number < desiredNumber && t.getMonth() == month) {
+ number++;
+ t = t.plusWeeks(1);
+ }
+ return t;
+ }
+
+ static ZonedDateTime nearestWeekday(ZonedDateTime t) {
+ if (t.getDayOfWeek() == DayOfWeek.SATURDAY) {
+ return t.minusDays(1);
+ } else if (t.getDayOfWeek() == DayOfWeek.SUNDAY) {
+ return t.plusDays(1);
+ }
+ return t;
+ }
+
+ static ZonedDateTime startOfYear() {
+ return midnight().withDayOfYear(1);
+ }
+}
diff --git a/src/test/java/org/xbib/time/schedule/DayOfMonthFieldTest.java b/src/test/java/org/xbib/time/schedule/DayOfMonthFieldTest.java
new file mode 100644
index 0000000..dc57632
--- /dev/null
+++ b/src/test/java/org/xbib/time/schedule/DayOfMonthFieldTest.java
@@ -0,0 +1,36 @@
+package org.xbib.time.schedule;
+
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import java.time.DayOfWeek;
+import java.time.ZonedDateTime;
+import java.time.temporal.TemporalAdjusters;
+
+public class DayOfMonthFieldTest {
+
+ @Test
+ public void last() {
+ DayOfMonthField field = parse("L");
+ assertTrue(field.matches(ZonedDateTime.now().with(TemporalAdjusters.lastDayOfMonth())));
+ }
+
+ @Test
+ public void nearestFriday() {
+ ZonedDateTime saturday = ZonedDateTime.now().with(DayOfWeek.SATURDAY);
+ ZonedDateTime friday = saturday.minusDays(1);
+ DayOfMonthField field = parse(saturday.getDayOfMonth() + "W");
+ assertTrue(field.matches(friday));
+ }
+
+ @Test
+ public void nearestMonday() {
+ ZonedDateTime sunday = ZonedDateTime.now().with(DayOfWeek.SUNDAY);
+ ZonedDateTime monday = sunday.plusDays(1);
+ DayOfMonthField field = parse(sunday.getDayOfMonth() + "W");
+ assertTrue(field.matches(monday));
+ }
+
+ private DayOfMonthField parse(String s) {
+ return DayOfMonthField.parse(new Tokens(s));
+ }
+}
diff --git a/src/test/java/org/xbib/time/schedule/DayOfWeekFieldTest.java b/src/test/java/org/xbib/time/schedule/DayOfWeekFieldTest.java
new file mode 100644
index 0000000..f862844
--- /dev/null
+++ b/src/test/java/org/xbib/time/schedule/DayOfWeekFieldTest.java
@@ -0,0 +1,28 @@
+package org.xbib.time.schedule;
+
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+
+public class DayOfWeekFieldTest {
+
+ @Test
+ public void keywords() {
+ assertTrue(parse("MON", false).contains(1));
+ assertTrue(parse("TUE", false).contains(2));
+ assertTrue(parse("WED", false).contains(3));
+ assertTrue(parse("THU", false).contains(4));
+ assertTrue(parse("FRI", false).contains(5));
+ assertTrue(parse("SAT", false).contains(6));
+ assertTrue(parse("SUN", false).contains(7));
+ }
+
+ @Test
+ public void oneBased() {
+ assertTrue(parse("1", true).contains(0));
+ assertTrue(parse("2", true).contains(1));
+ }
+
+ private DayOfWeekField parse(String s, boolean oneBased) {
+ return DayOfWeekField.parse(new Tokens(s), oneBased);
+ }
+}
diff --git a/src/test/java/org/xbib/time/schedule/DefaultFieldTest.java b/src/test/java/org/xbib/time/schedule/DefaultFieldTest.java
new file mode 100644
index 0000000..fdbb3a9
--- /dev/null
+++ b/src/test/java/org/xbib/time/schedule/DefaultFieldTest.java
@@ -0,0 +1,125 @@
+package org.xbib.time.schedule;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+
+public class DefaultFieldTest {
+
+ private DefaultField field;
+
+ @Test
+ public void emptyMonthField() {
+ parse("", 1, 12);
+ for (int month = 1; month <= 12; month++) {
+ assertFalse(field.contains(month));
+ }
+ }
+
+ @Test
+ public void backwardsRange() {
+ parse("2-1", 1, 2);
+ assertFalse(field.contains(0));
+ assertFalse(field.contains(1));
+ assertFalse(field.contains(2));
+ assertFalse(field.contains(3));
+ }
+
+ @Test
+ public void finalWildcardWins() {
+ parse("1-2,2,3,*", 1, 10);
+ assertContainsRange(1, 10);
+ }
+
+ @Test
+ public void initialWildcardWins() {
+ parse("*,1-2,2,3", 1, 10);
+ assertContainsRange(1, 10);
+ }
+
+ @Test
+ public void multipleRanges() {
+ parse("1-2,3-4", 1, 5);
+ assertContains(1, 2, 3, 4);
+ }
+
+ @Test
+ public void multipleNumbers() {
+ parse("1,2,3", 1, 5);
+ assertContains(1, 2, 3);
+ }
+
+ @Test
+ public void danglingRange() {
+ try {
+ parse("1-", 0, 0);
+ fail("Expected exception");
+ } catch (IllegalStateException e) {
+ assertEquals("Expected number", e.getMessage());
+ }
+ }
+
+ @Test
+ public void danglingSkip() {
+ try {
+ parse("1-2/", 0, 0);
+ fail("Expected exception");
+ } catch (IllegalStateException e) {
+ assertEquals("Expected number", e.getMessage());
+ }
+ }
+
+ @Test
+ public void range() {
+ parse("1-12", 1, 12);
+ assertContainsRange(1, 12);
+ }
+
+ @Test
+ public void wildcard() {
+ parse("*", 1, 12);
+ assertContainsRange(1, 12);
+ }
+
+ @Test
+ public void skipRangeWithImplicitEnd() {
+ parse("1/5", 1, 31);
+ assertContains(1, 6, 11, 16, 21, 26, 31);
+ }
+
+ @Test
+ public void oneBasedSkipRange() {
+ parse("1-31/5", 1, 31);
+ assertContains(1, 6, 11, 16, 21, 26, 31);
+ }
+
+ @Test
+ public void zeroBasedSkipRange() {
+ parse("0-20/5", 0, 59);
+ assertContains(0, 5, 10, 15, 20);
+ }
+
+ @Test
+ public void wildcardSkipRange() {
+ parse("*/5", 0, 20);
+ assertContains(0, 5, 10, 15, 20);
+ }
+
+ private void assertContains(int... numbers) {
+ for (int number : numbers) {
+ assertTrue(field.contains(number));
+ }
+ }
+
+ private void assertContainsRange(int first, int last) {
+ for (int number = first; number <= last; number++) {
+ assertContains(number);
+ }
+ }
+
+ private DefaultField parse(String s, int min, int max) {
+ return field = DefaultField.parse(new Tokens(s), min, max);
+ }
+}
diff --git a/src/test/java/org/xbib/time/schedule/Integers.java b/src/test/java/org/xbib/time/schedule/Integers.java
new file mode 100644
index 0000000..677e43e
--- /dev/null
+++ b/src/test/java/org/xbib/time/schedule/Integers.java
@@ -0,0 +1,42 @@
+package org.xbib.time.schedule;
+
+import com.google.common.collect.ForwardingSet;
+import java.util.HashSet;
+import java.util.Set;
+
+public class Integers extends ForwardingSet {
+ private final Set delegate;
+
+ public Integers(int... integers) {
+ delegate = new HashSet<>();
+ with(integers);
+ }
+
+ @Override
+ protected Set delegate() {
+ return delegate;
+ }
+
+ public Integers with(int... integers) {
+ for (int integer : integers) {
+ add(integer);
+ }
+ return this;
+ }
+
+ public Integers withRange(int start, int end) {
+ for (int i = start; i <= end; i++) {
+ add(i);
+ }
+ return this;
+ }
+
+ public Integers withRange(int start, int end, int mod) {
+ for (int i = start; i <= end; i++) {
+ if ((i - start) % mod == 0) {
+ add(i);
+ }
+ }
+ return this;
+ }
+}
diff --git a/src/test/java/org/xbib/time/schedule/KeywordsTest.java b/src/test/java/org/xbib/time/schedule/KeywordsTest.java
new file mode 100644
index 0000000..8767ca2
--- /dev/null
+++ b/src/test/java/org/xbib/time/schedule/KeywordsTest.java
@@ -0,0 +1,79 @@
+package org.xbib.time.schedule;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import org.junit.Before;
+import org.junit.Test;
+
+public class KeywordsTest {
+ private Keywords keywords;
+
+ @Before
+ public void before() {
+ keywords = new Keywords();
+ }
+
+ @Test
+ public void normalUse() {
+ keywords.put("AAA", 1);
+ keywords.put("BBB", 2);
+ assertEquals(1, keywords.get("AAABBB", 0, 3));
+ assertEquals(2, keywords.get("AAABBB", 3, 6));
+ }
+
+ @Test
+ public void getNotPresent() {
+ try {
+ assertEquals(-1, keywords.get("CCC", 0, 3));
+ fail("Expected exception");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void getWrongAlphabet() {
+ try {
+ keywords.get("aaa", 0, 3);
+ fail("Expected exception");
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void getWrongLength() {
+ try {
+ keywords.get("aaa", 0, 1);
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ @Test
+ public void putEmpty() {
+ try {
+ keywords.put("", 0);
+ fail("Expected exception");
+ } catch (StringIndexOutOfBoundsException e) {
+ assertEquals("String index out of range: 0", e.getMessage());
+ }
+ }
+
+ @Test
+ public void putWrongLength() {
+ try {
+ keywords.put("A", 0);
+ fail("Expected exception");
+ } catch (StringIndexOutOfBoundsException e) {
+ assertEquals("String index out of range: 1", e.getMessage());
+ }
+ }
+
+ @Test
+ public void putWrongAlphabet() {
+ try {
+ keywords.put("a", 0);
+ fail("Expected exception");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ assertEquals("Index 32 out of bounds for length 26", e.getMessage());
+ }
+ }
+}
diff --git a/src/test/java/org/xbib/time/schedule/MonthFieldTest.java b/src/test/java/org/xbib/time/schedule/MonthFieldTest.java
new file mode 100644
index 0000000..83d161b
--- /dev/null
+++ b/src/test/java/org/xbib/time/schedule/MonthFieldTest.java
@@ -0,0 +1,27 @@
+package org.xbib.time.schedule;
+
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+
+public class MonthFieldTest {
+
+ @Test
+ public void keywords() {
+ assertTrue(parse("JAN").contains(1));
+ assertTrue(parse("FEB").contains(2));
+ assertTrue(parse("MAR").contains(3));
+ assertTrue(parse("APR").contains(4));
+ assertTrue(parse("MAY").contains(5));
+ assertTrue(parse("JUN").contains(6));
+ assertTrue(parse("JUL").contains(7));
+ assertTrue(parse("AUG").contains(8));
+ assertTrue(parse("SEP").contains(9));
+ assertTrue(parse("OCT").contains(10));
+ assertTrue(parse("NOV").contains(11));
+ assertTrue(parse("DEC").contains(12));
+ }
+
+ private DefaultField parse(String s) {
+ return MonthField.parse(new Tokens(s));
+ }
+}
diff --git a/src/test/java/org/xbib/time/schedule/NextExecutionTest.java b/src/test/java/org/xbib/time/schedule/NextExecutionTest.java
new file mode 100644
index 0000000..e931a13
--- /dev/null
+++ b/src/test/java/org/xbib/time/schedule/NextExecutionTest.java
@@ -0,0 +1,79 @@
+package org.xbib.time.schedule;
+
+import org.junit.Test;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class NextExecutionTest {
+
+ private static final Logger logger = Logger.getLogger(NextExecutionTest.class.getName());
+
+ @Test
+ public void nextSecond() {
+ CronExpression expression = CronExpression.parser().withSecondsField(true)
+ .parse("0-59 * * * * *");
+ ZonedDateTime now = ZonedDateTime.now();
+ ZonedDateTime next = expression.nextExecution(now, now.plusMinutes(1));
+ logger.log(Level.INFO, now.toString());
+ logger.log(Level.INFO, next.toString());
+ }
+
+ @Test
+ public void nextHour() {
+ CronExpression expression = CronExpression.hourly();
+ ZonedDateTime now = ZonedDateTime.now();
+ ZonedDateTime next = expression.nextExecution(now, now.plusHours(2));
+ logger.log(Level.INFO, now.toString());
+ logger.log(Level.INFO, next.toString());
+ }
+
+ @Test
+ public void nextDay() {
+ CronExpression expression = CronExpression.daily();
+ ZonedDateTime now = ZonedDateTime.now();
+ ZonedDateTime next = expression.nextExecution(now, now.plusDays(2));
+ logger.log(Level.INFO, now.toString());
+ logger.log(Level.INFO, next.toString());
+ }
+
+ @Test
+ public void nextWeek() {
+ CronExpression expression = CronExpression.weekly();
+ ZonedDateTime now = ZonedDateTime.now();
+ ZonedDateTime next = expression.nextExecution(now, now.plusWeeks(2));
+ logger.log(Level.INFO, now.toString());
+ logger.log(Level.INFO, next.toString());
+ }
+
+ @Test
+ public void nextMonth() {
+ CronExpression expression = CronExpression.monthly();
+ ZonedDateTime now = ZonedDateTime.now();
+ ZonedDateTime next = expression.nextExecution(now, now.plusMonths(2));
+ logger.log(Level.INFO, now.toString());
+ logger.log(Level.INFO, next.toString());
+ }
+
+ @Test
+ public void nextFractionMinute() {
+ CronExpression expression = CronExpression.parse("10-20/5 * * * * *");
+ ZonedDateTime now = ZonedDateTime.now();
+ ZonedDateTime next = expression.nextExecution(now, now.plusHours(2));
+ logger.log(Level.INFO, now.toString());
+ logger.log(Level.INFO, next.toString());
+ }
+
+ @Test
+ public void nextMultipleNth() {
+ CronExpression expression = CronExpression.parser()
+ .withSecondsField(true)
+ .parse("0/5 14,18,3-39,52 * ? JAN,MAR,SEP MON-FRI 2002-2010");
+ ZonedDateTime begin = ZonedDateTime.of(2002, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault());
+ logger.log(Level.INFO, expression.toString());
+ logger.log(Level.INFO, begin.toString());
+ ZonedDateTime next = expression.nextExecution(begin, begin.plusYears(1));
+ logger.log(Level.INFO, next.toString());
+ }
+}
diff --git a/src/test/java/org/xbib/time/schedule/ObjectSizeCalculator.java b/src/test/java/org/xbib/time/schedule/ObjectSizeCalculator.java
new file mode 100644
index 0000000..4435833
--- /dev/null
+++ b/src/test/java/org/xbib/time/schedule/ObjectSizeCalculator.java
@@ -0,0 +1,436 @@
+package org.xbib.time.schedule;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.Sets;
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryPoolMXBean;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Contains utility methods for calculating the memory usage of objects. It
+ * only works on the HotSpot JVM, and infers the actual memory layout (32 bit
+ * vs. 64 bit word size, compressed object pointers vs. uncompressed) from
+ * best available indicators. It can reliably detect a 32 bit vs. 64 bit JVM.
+ * It can only make an educated guess at whether compressed OOPs are used,
+ * though; specifically, it knows what the JVM's default choice of OOP
+ * compression would be based on HotSpot version and maximum heap sizes, but if
+ * the choice is explicitly overridden with the -XX:{+|-}UseCompressedOops command line
+ * switch, it can not detect
+ * this fact and will report incorrect sizes, as it will presume the default JVM
+ * behavior.
+ */
+public class ObjectSizeCalculator {
+
+ /**
+ * Describes constant memory overheads for various constructs in a JVM implementation.
+ */
+ public interface MemoryLayoutSpecification {
+
+ /**
+ * Returns the fixed overhead of an array of any type or length in this JVM.
+ *
+ * @return the fixed overhead of an array.
+ */
+ int getArrayHeaderSize();
+
+ /**
+ * Returns the fixed overhead of for any {@link Object} subclass in this JVM.
+ *
+ * @return the fixed overhead of any object.
+ */
+ int getObjectHeaderSize();
+
+ /**
+ * Returns the quantum field size for a field owned by an object in this JVM.
+ *
+ * @return the quantum field size for an object.
+ */
+ int getObjectPadding();
+
+ /**
+ * Returns the fixed size of an object reference in this JVM.
+ *
+ * @return the size of all object references.
+ */
+ int getReferenceSize();
+
+ /**
+ * Returns the quantum field size for a field owned by one of an object's ancestor superclasses
+ * in this JVM.
+ *
+ * @return the quantum field size for a superclass field.
+ */
+ int getSuperclassFieldPadding();
+ }
+
+ private static class CurrentLayout {
+ private static final MemoryLayoutSpecification SPEC =
+ getEffectiveMemoryLayoutSpecification();
+ }
+
+ /**
+ * Given an object, returns the total allocated size, in bytes, of the object
+ * and all other objects reachable from it. Attempts to to detect the current JVM memory layout,
+ * but may fail with {@link UnsupportedOperationException};
+ *
+ * @param obj the object; can be null. Passing in a {@link Class} object doesn't do
+ * anything special, it measures the size of all objects
+ * reachable through it (which will include its class loader, and by
+ * extension, all other Class objects loaded by
+ * the same loader, and all the parent class loaders). It doesn't provide the
+ * size of the static fields in the JVM class that the Class object
+ * represents.
+ * @return the total allocated size of the object and all other objects it
+ * retains.
+ * @throws UnsupportedOperationException if the current vm memory layout cannot be detected.
+ */
+ public static long getObjectSize(Object obj) throws UnsupportedOperationException {
+ return obj == null ? 0 : new ObjectSizeCalculator(CurrentLayout.SPEC).calculateObjectSize(obj);
+ }
+
+ // Fixed object header size for arrays.
+ private final int arrayHeaderSize;
+ // Fixed object header size for non-array objects.
+ private final int objectHeaderSize;
+ // Padding for the object size - if the object size is not an exact multiple
+ // of this, it is padded to the next multiple.
+ private final int objectPadding;
+ // Size of reference (pointer) fields.
+ private final int referenceSize;
+ // Padding for the fields of superclass before fields of subclasses are
+ // added.
+ private final int superclassFieldPadding;
+
+ private final LoadingCache, ClassSizeInfo> classSizeInfos =
+ CacheBuilder.newBuilder().build(new CacheLoader, ClassSizeInfo>() {
+ public ClassSizeInfo load(Class> clazz) {
+ return new ClassSizeInfo(clazz);
+ }
+ });
+
+
+ private final Set alreadyVisited = Sets.newIdentityHashSet();
+ private final Deque pending = new ArrayDeque(16 * 1024);
+ private long size;
+
+ /**
+ * Creates an object size calculator that can calculate object sizes for a given
+ * {@code memoryLayoutSpecification}.
+ *
+ * @param memoryLayoutSpecification a description of the JVM memory layout.
+ */
+ public ObjectSizeCalculator(MemoryLayoutSpecification memoryLayoutSpecification) {
+ Preconditions.checkNotNull(memoryLayoutSpecification);
+ arrayHeaderSize = memoryLayoutSpecification.getArrayHeaderSize();
+ objectHeaderSize = memoryLayoutSpecification.getObjectHeaderSize();
+ objectPadding = memoryLayoutSpecification.getObjectPadding();
+ referenceSize = memoryLayoutSpecification.getReferenceSize();
+ superclassFieldPadding = memoryLayoutSpecification.getSuperclassFieldPadding();
+ }
+
+ /**
+ * Given an object, returns the total allocated size, in bytes, of the object
+ * and all other objects reachable from it.
+ *
+ * @param obj the object; can be null. Passing in a {@link Class} object doesn't do
+ * anything special, it measures the size of all objects
+ * reachable through it (which will include its class loader, and by
+ * extension, all other Class objects loaded by
+ * the same loader, and all the parent class loaders). It doesn't provide the
+ * size of the static fields in the JVM class that the Class object
+ * represents.
+ * @return the total allocated size of the object and all other objects it
+ * retains.
+ */
+ public synchronized long calculateObjectSize(Object obj) {
+ // Breadth-first traversal instead of naive depth-first with recursive
+ // implementation, so we don't blow the stack traversing long linked lists.
+ try {
+ for (; ; ) {
+ visit(obj);
+ if (pending.isEmpty()) {
+ return size;
+ }
+ obj = pending.removeFirst();
+ }
+ } finally {
+ alreadyVisited.clear();
+ pending.clear();
+ size = 0;
+ }
+ }
+
+ private void visit(Object obj) {
+ if (alreadyVisited.contains(obj)) {
+ return;
+ }
+ final Class> clazz = obj.getClass();
+ if (clazz == ArrayElementsVisitor.class) {
+ ((ArrayElementsVisitor) obj).visit(this);
+ } else {
+ alreadyVisited.add(obj);
+ if (clazz.isArray()) {
+ visitArray(obj);
+ } else {
+ classSizeInfos.getUnchecked(clazz).visit(obj, this);
+ }
+ }
+ }
+
+ private void visitArray(Object array) {
+ final Class> componentType = array.getClass().getComponentType();
+ final int length = Array.getLength(array);
+ if (componentType.isPrimitive()) {
+ increaseByArraySize(length, getPrimitiveFieldSize(componentType));
+ } else {
+ increaseByArraySize(length, referenceSize);
+ // If we didn't use an ArrayElementsVisitor, we would be enqueueing every
+ // element of the array here instead. For large arrays, it would
+ // tremendously enlarge the queue. In essence, we're compressing it into
+ // a small command object instead. This is different than immediately
+ // visiting the elements, as their visiting is scheduled for the end of
+ // the current queue.
+ switch (length) {
+ case 0: {
+ break;
+ }
+ case 1: {
+ enqueue(Array.get(array, 0));
+ break;
+ }
+ default: {
+ enqueue(new ArrayElementsVisitor((Object[]) array));
+ }
+ }
+ }
+ }
+
+ private void increaseByArraySize(int length, long elementSize) {
+ increaseSize(roundTo(arrayHeaderSize + length * elementSize, objectPadding));
+ }
+
+ private static class ArrayElementsVisitor {
+ private final Object[] array;
+
+ ArrayElementsVisitor(Object[] array) {
+ this.array = array;
+ }
+
+ public void visit(ObjectSizeCalculator calc) {
+ for (Object elem : array) {
+ if (elem != null) {
+ calc.visit(elem);
+ }
+ }
+ }
+ }
+
+ void enqueue(Object obj) {
+ if (obj != null) {
+ pending.addLast(obj);
+ }
+ }
+
+ void increaseSize(long objectSize) {
+ size += objectSize;
+ }
+
+ @VisibleForTesting
+ static long roundTo(long x, int multiple) {
+ return ((x + multiple - 1) / multiple) * multiple;
+ }
+
+ private class ClassSizeInfo {
+ // Padded fields + header size
+ private final long objectSize;
+ // Only the fields size - used to calculate the subclasses' memory
+ // footprint.
+ private final long fieldsSize;
+ private final Field[] referenceFields;
+
+ public ClassSizeInfo(Class> clazz) {
+ long fieldsSize = 0;
+ final List referenceFields = new LinkedList();
+ for (Field f : clazz.getDeclaredFields()) {
+ if (Modifier.isStatic(f.getModifiers())) {
+ continue;
+ }
+ final Class> type = f.getType();
+ if (type.isPrimitive()) {
+ fieldsSize += getPrimitiveFieldSize(type);
+ } else {
+ f.setAccessible(true);
+ referenceFields.add(f);
+ fieldsSize += referenceSize;
+ }
+ }
+ final Class> superClass = clazz.getSuperclass();
+ if (superClass != null) {
+ final ClassSizeInfo superClassInfo = classSizeInfos.getUnchecked(superClass);
+ fieldsSize += roundTo(superClassInfo.fieldsSize, superclassFieldPadding);
+ referenceFields.addAll(Arrays.asList(superClassInfo.referenceFields));
+ }
+ this.fieldsSize = fieldsSize;
+ this.objectSize = roundTo(objectHeaderSize + fieldsSize, objectPadding);
+ this.referenceFields = referenceFields.toArray(
+ new Field[referenceFields.size()]);
+ }
+
+ void visit(Object obj, ObjectSizeCalculator calc) {
+ calc.increaseSize(objectSize);
+ enqueueReferencedObjects(obj, calc);
+ }
+
+ public void enqueueReferencedObjects(Object obj, ObjectSizeCalculator calc) {
+ for (Field f : referenceFields) {
+ try {
+ calc.enqueue(f.get(obj));
+ } catch (IllegalAccessException e) {
+ final AssertionError ae = new AssertionError(
+ "Unexpected denial of access to " + f, e);
+ throw ae;
+ }
+ }
+ }
+ }
+
+ private static long getPrimitiveFieldSize(Class> type) {
+ if (type == boolean.class || type == byte.class) {
+ return 1;
+ }
+ if (type == char.class || type == short.class) {
+ return 2;
+ }
+ if (type == int.class || type == float.class) {
+ return 4;
+ }
+ if (type == long.class || type == double.class) {
+ return 8;
+ }
+ throw new AssertionError("Encountered unexpected primitive type " +
+ type.getName());
+ }
+
+ @VisibleForTesting
+ static MemoryLayoutSpecification getEffectiveMemoryLayoutSpecification() {
+ final String vmName = System.getProperty("java.vm.name");
+ if (vmName == null || !(vmName.startsWith("Java HotSpot(TM) ") || vmName.startsWith("OpenJDK"))) {
+ throw new UnsupportedOperationException(
+ "ObjectSizeCalculator only supported on HotSpot VM");
+ }
+
+ final String dataModel = System.getProperty("sun.arch.data.model");
+ if ("32".equals(dataModel)) {
+ // Running with 32-bit data model
+ return new MemoryLayoutSpecification() {
+ @Override
+ public int getArrayHeaderSize() {
+ return 12;
+ }
+
+ @Override
+ public int getObjectHeaderSize() {
+ return 8;
+ }
+
+ @Override
+ public int getObjectPadding() {
+ return 8;
+ }
+
+ @Override
+ public int getReferenceSize() {
+ return 4;
+ }
+
+ @Override
+ public int getSuperclassFieldPadding() {
+ return 4;
+ }
+ };
+ } else if (!"64".equals(dataModel)) {
+ throw new UnsupportedOperationException("Unrecognized value '" +
+ dataModel + "' of sun.arch.data.model system property");
+ }
+
+ final String strVmVersion = System.getProperty("java.vm.version");
+ final int vmVersion = Integer.parseInt(strVmVersion.substring(0,
+ strVmVersion.indexOf('.')));
+ if (vmVersion >= 17) {
+ long maxMemory = 0;
+ for (MemoryPoolMXBean mp : ManagementFactory.getMemoryPoolMXBeans()) {
+ maxMemory += mp.getUsage().getMax();
+ }
+ if (maxMemory < 30L * 1024 * 1024 * 1024) {
+ // HotSpot 17.0 and above use compressed OOPs below 30GB of RAM total
+ // for all memory pools (yes, including code cache).
+ return new MemoryLayoutSpecification() {
+ @Override
+ public int getArrayHeaderSize() {
+ return 16;
+ }
+
+ @Override
+ public int getObjectHeaderSize() {
+ return 12;
+ }
+
+ @Override
+ public int getObjectPadding() {
+ return 8;
+ }
+
+ @Override
+ public int getReferenceSize() {
+ return 4;
+ }
+
+ @Override
+ public int getSuperclassFieldPadding() {
+ return 4;
+ }
+ };
+ }
+ }
+
+ // In other cases, it's a 64-bit uncompressed OOPs object model
+ return new MemoryLayoutSpecification() {
+ @Override
+ public int getArrayHeaderSize() {
+ return 24;
+ }
+
+ @Override
+ public int getObjectHeaderSize() {
+ return 16;
+ }
+
+ @Override
+ public int getObjectPadding() {
+ return 8;
+ }
+
+ @Override
+ public int getReferenceSize() {
+ return 8;
+ }
+
+ @Override
+ public int getSuperclassFieldPadding() {
+ return 8;
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/org/xbib/time/schedule/ReadmeTest.java b/src/test/java/org/xbib/time/schedule/ReadmeTest.java
new file mode 100644
index 0000000..369d54b
--- /dev/null
+++ b/src/test/java/org/xbib/time/schedule/ReadmeTest.java
@@ -0,0 +1,38 @@
+package org.xbib.time.schedule;
+
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
+
+/**
+ * Examples used in the Readme file.
+ */
+public class ReadmeTest {
+
+ @Test
+ public void normal() {
+ ZonedDateTime time = ZonedDateTime.now().withDayOfYear(1).truncatedTo(ChronoUnit.DAYS);
+ assertTrue(CronExpression.parse("0 0 1 1 *").matches(time));
+ assertTrue(CronExpression.parse("@yearly").matches(time));
+ assertTrue(CronExpression.parse("@annually").matches(time));
+ assertTrue(CronExpression.yearly().matches(time));
+ }
+
+ @Test
+ public void quartzLike() {
+ CronExpression expression = CronExpression.parser()
+ .withSecondsField(true)
+ .withOneBasedDayOfWeek(true)
+ .allowBothDayFields(false)
+ .parse("0 15 10 L * ?");
+ ZonedDateTime time = ZonedDateTime.now().truncatedTo(ChronoUnit.DAYS)
+ .withYear(2013)
+ .withMonth(1)
+ .withDayOfMonth(31)
+ .withHour(10)
+ .withMinute(15);
+ assertTrue(expression.matches(time));
+ }
+
+}
diff --git a/src/test/java/org/xbib/time/schedule/Times.java b/src/test/java/org/xbib/time/schedule/Times.java
new file mode 100644
index 0000000..c3c13e4
--- /dev/null
+++ b/src/test/java/org/xbib/time/schedule/Times.java
@@ -0,0 +1,102 @@
+package org.xbib.time.schedule;
+
+import com.google.common.collect.ImmutableSortedSet;
+import java.time.DayOfWeek;
+import java.time.Month;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalAdjusters;
+import java.util.Iterator;
+import java.util.NavigableSet;
+
+public class Times {
+
+ final Integers
+ seconds,
+ minutes,
+ hours,
+ months,
+ daysOfWeek,
+ years,
+ daysOfMonth;
+
+ Times() {
+ seconds = new Integers();
+ minutes = new Integers();
+ hours = new Integers();
+ months = new Integers();
+ daysOfWeek = new Integers();
+ years = new Integers();
+ daysOfMonth = new Integers();
+ }
+
+ NavigableSet dateTimes() {
+ if (seconds.isEmpty()) {
+ seconds.withRange(0, 1);
+ }
+ if (minutes.isEmpty()) {
+ minutes.withRange(0, 1);
+ }
+ if (hours.isEmpty()) {
+ hours.withRange(0, 1);
+ }
+ if (months.isEmpty()) {
+ months.withRange(1, 2);
+ }
+ if (years.isEmpty()) {
+ int thisYear = ZonedDateTime.now().getYear();
+ years.withRange(thisYear, thisYear + 1);
+ }
+ ImmutableSortedSet.Builder builder = ImmutableSortedSet.naturalOrder();
+ for (int second : seconds) {
+ for (int minute : minutes) {
+ for (int hour : hours) {
+ for (int month : months) {
+ for (int year : years) {
+ ZonedDateTime base = ZonedDateTime.now()
+ .truncatedTo(ChronoUnit.DAYS)
+ .withSecond(second)
+ .withMinute(minute)
+ .withHour(hour)
+ .withMonth(month)
+ .withDayOfMonth(1)
+ .withYear(year);
+ if (!daysOfWeek.isEmpty() && !daysOfMonth.isEmpty()) {
+ addDaysOfWeek(builder, base);
+ addDaysOfMonth(builder, base);
+ } else if (!daysOfWeek.isEmpty()) {
+ addDaysOfWeek(builder, base);
+ } else if (!daysOfMonth.isEmpty()) {
+ addDaysOfMonth(builder, base);
+ } else {
+ builder.add(base);
+ }
+ }
+ }
+ }
+ }
+ }
+ return builder.build();
+ }
+
+ private void addDaysOfWeek(ImmutableSortedSet.Builder builder, ZonedDateTime base) {
+ Month month = base.getMonth();
+ Iterator iterator = daysOfWeek.iterator();
+ base = base.with(DayOfWeek.of(iterator.next()));
+ if (base.getMonth() != month) {
+ base = base.plusWeeks(1);
+ }
+ do {
+ builder.add(base);
+ base = base.plusWeeks(1);
+ } while (base.getMonth() == month);
+ }
+
+ private void addDaysOfMonth(ImmutableSortedSet.Builder builder, ZonedDateTime base) {
+ for (int day : daysOfMonth) {
+ if (day <= base.with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth()) {
+ builder.add(base.withDayOfMonth(day));
+ }
+ }
+ }
+}
diff --git a/src/test/java/org/xbib/time/schedule/TokensTest.java b/src/test/java/org/xbib/time/schedule/TokensTest.java
new file mode 100644
index 0000000..86493db
--- /dev/null
+++ b/src/test/java/org/xbib/time/schedule/TokensTest.java
@@ -0,0 +1,204 @@
+package org.xbib.time.schedule;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import org.junit.Test;
+
+public class TokensTest {
+ private Tokens tokens;
+
+ @Test
+ public void empty() {
+ tokenize("");
+ assertFalse(tokens.hasNext());
+ assertEndOfInput();
+ }
+
+ @Test
+ public void end() {
+ tokenize("1");
+ assertNextIsNumber(1);
+ assertFalse(tokens.hasNext());
+ assertEndOfInput();
+ }
+
+ @Test
+ public void offset() {
+ tokenize("5,6");
+ tokens.offset(5);
+ assertNextIsNumber(0);
+ assertNextIs(Token.VALUE_SEPARATOR);
+ assertNextIsNumber(1);
+ assertEndOfInput();
+ }
+
+ @Test
+ public void resetClearsOffset() {
+ tokenize("2,2");
+ tokens.offset(1);
+ assertNextIsNumber(1);
+ assertNextIs(Token.VALUE_SEPARATOR);
+ tokens.reset();
+ assertNextIsNumber(2);
+ }
+
+ @Test
+ public void resetClearsKeywords() {
+ tokenize("FRI,FRI");
+ tokens.keywords(DayOfWeekField.Builder.KEYWORDS);
+ assertNextIsNumber(5);
+ assertNextIs(Token.VALUE_SEPARATOR);
+ tokens.reset();
+ try {
+ tokens.next();
+ fail("Expected exception");
+ } catch (IllegalArgumentException e) {
+ assertEquals("Bad keyword 'FRI' at position 4 in string: FRI,FRI", e.getMessage());
+ }
+ }
+
+ @Test
+ public void matchOne() {
+ tokenize("?");
+ assertNextIs(Token.MATCH_ONE);
+ assertEndOfInput();
+ }
+
+ @Test
+ public void matchAll() {
+ tokenize("*");
+ assertNextIs(Token.MATCH_ALL);
+ assertEndOfInput();
+ }
+
+ @Test
+ public void skip() {
+ tokenize("/");
+ assertNextIs(Token.SKIP);
+ assertEndOfInput();
+ }
+
+ @Test
+ public void range() {
+ tokenize("-");
+ assertNextIs(Token.RANGE);
+ assertEndOfInput();
+ }
+
+ @Test
+ public void last() {
+ tokenize("1L");
+ assertNextIsNumber(1);
+ assertNextIs(Token.LAST);
+ assertEndOfInput();
+ }
+
+ @Test
+ public void lastAlone() {
+ tokenize("L");
+ assertNextIs(Token.LAST);
+ assertEndOfInput();
+ }
+
+ @Test
+ public void weekday() {
+ tokenize("1W");
+ assertNextIsNumber(1);
+ assertNextIs(Token.WEEKDAY);
+ assertEndOfInput();
+ }
+
+ @Test
+ public void nth() {
+ tokenize("1#2");
+ assertNextIsNumber(1);
+ assertNextIs(Token.NTH);
+ assertNextIsNumber(2);
+ assertEndOfInput();
+ }
+
+ @Test
+ public void multipleWhitespaceCharacters() {
+ tokenize(" \t \t \t \t ");
+ assertEquals(Token.FIELD_SEPARATOR, tokens.next());
+ assertEndOfInput();
+ }
+
+ @Test
+ public void keywordRange() {
+ tokenize("MON-FRI");
+ tokens.keywords(DayOfWeekField.Builder.KEYWORDS);
+ assertEquals(Token.NUMBER, tokens.next());
+ assertEquals(1, tokens.number());
+ assertEquals(Token.RANGE, tokens.next());
+ assertEquals(Token.NUMBER, tokens.next());
+ assertEquals(5, tokens.number());
+ assertEndOfInput();
+ }
+
+ @Test
+ public void badCharacter() {
+ tokenize("5%");
+ assertEquals(Token.NUMBER, tokens.next());
+ try {
+ tokens.next();
+ fail("Expected exception");
+ } catch (IllegalArgumentException e) {
+ assertEquals("Bad character '%' at position 1 in string: 5%", e.getMessage());
+ }
+ }
+
+ @Test
+ public void badLetter() {
+ tokenize("1F");
+ assertEquals(Token.NUMBER, tokens.next());
+ try {
+ tokens.next();
+ fail("Expected exception");
+ } catch (IllegalArgumentException e) {
+ assertEquals("Bad character 'F' at position 1 in string: 1F", e.getMessage());
+ }
+ }
+
+ @Test
+ public void badKeywordOfValidLength() {
+ tokenize("ABC");
+ tokens.keywords(DayOfWeekField.Builder.KEYWORDS);
+ try {
+ tokens.next();
+ fail("Expected exception");
+ } catch (IllegalArgumentException e) {
+ assertEquals("Bad keyword 'ABC' at position 0 in string: ABC", e.getMessage());
+ }
+ }
+
+ @Test
+ public void badKeywordOfInvalidLength() {
+ tokenize("AB");
+ tokens.keywords(DayOfWeekField.Builder.KEYWORDS);
+ try {
+ tokens.next();
+ fail("Expected exception");
+ } catch (IllegalArgumentException e) {
+ assertEquals("Bad keyword 'AB' at position 0 in string: AB", e.getMessage());
+ }
+ }
+
+ private void assertEndOfInput() {
+ assertNextIs(Token.END_OF_INPUT);
+ }
+
+ private void assertNextIsNumber(int expected) {
+ assertNextIs(Token.NUMBER);
+ assertEquals(expected, tokens.number());
+ }
+
+ private void assertNextIs(Token expected) {
+ assertEquals(expected, tokens.next());
+ }
+
+ private void tokenize(String s) {
+ tokens = new Tokens(s);
+ }
+}
diff --git a/src/test/java/org/xbib/time/schedule/WhatQuartzDoesNotSupport.java b/src/test/java/org/xbib/time/schedule/WhatQuartzDoesNotSupport.java
new file mode 100644
index 0000000..af33881
--- /dev/null
+++ b/src/test/java/org/xbib/time/schedule/WhatQuartzDoesNotSupport.java
@@ -0,0 +1,53 @@
+package org.xbib.time.schedule;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.xbib.time.schedule.DateTimes.nthOfMonth;
+import org.junit.Test;
+import java.text.ParseException;
+import java.time.DayOfWeek;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class WhatQuartzDoesNotSupport {
+ @Test
+ public void multipleNthDayOfWeek() {
+ try {
+ org.quartz.CronExpression quartz = new org.quartz.CronExpression("0 0 0 ? * 6#3,4#1,3#2");
+ List times = new ArrayList<>();
+ ZonedDateTime t = ZonedDateTime.now().truncatedTo(ChronoUnit.DAYS).withDayOfYear(1);
+ int year = t.getYear();
+ while (t.getYear() == year) {
+ times.add(nthOfMonth(t, DayOfWeek.FRIDAY, 3));
+ times.add(nthOfMonth(t, DayOfWeek.TUESDAY, 2));
+ t = t.plusMonths(1);
+ }
+ for (ZonedDateTime time : times) {
+ boolean satisfied = quartz.isSatisfiedBy(Date.from(time.toInstant()));
+ if (time.getDayOfWeek() == DayOfWeek.TUESDAY) {
+ // Earlier versions of Quartz only picked up the last one
+ assertTrue(satisfied);
+ } else {
+ assertFalse(satisfied);
+ }
+ }
+ } catch (ParseException e) {
+ assertEquals("Support for specifying multiple \"nth\" days is not implemented.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void multipleLastDayOfWeek() throws Exception {
+ try {
+ new org.quartz.CronExpression("0 0 0 ? * 6L,4L,3L");
+ fail("Expected exception");
+ } catch (ParseException e) {
+ assertEquals("Support for specifying 'L' with other days of the week is not implemented", e.getMessage());
+ }
+ }
+}
diff --git a/src/test/resources/log4j2.xml b/src/test/resources/log4j2.xml
deleted file mode 100644
index f71aced..0000000
--- a/src/test/resources/log4j2.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file