CAUSEWAY-3743: adds TimeZoneChoiceProvider SPI to customize zone choices

- for e.g. Wicket Viewer Login
diff --git a/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/TemporalCharacteristicsProvider.java b/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/TemporalCharacteristicsProvider.java
index 88d5805..597597e 100644
--- a/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/TemporalCharacteristicsProvider.java
+++ b/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/TemporalCharacteristicsProvider.java
@@ -18,15 +18,7 @@
  */
 package org.apache.causeway.applib.value.semantics;
 
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.time.ZoneOffset;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import lombok.val;
-
-public interface TemporalCharacteristicsProvider {
+public interface TemporalCharacteristicsProvider extends TimeZoneChoiceProvider {
 
     static enum TemporalCharacteristic {
 
@@ -70,27 +62,5 @@
 
     TemporalCharacteristic getTemporalCharacteristic();
     OffsetCharacteristic getOffsetCharacteristic();
-
-    /**
-     * For temporal value editing, provides the list of available time zones to choose from.
-     */
-    default List<ZoneId> getAvailableZoneIds() {
-        return ZoneId.getAvailableZoneIds().stream()
-            .sorted()
-            .map(ZoneId::of)
-            .collect(Collectors.toList());
-    }
-
-    /**
-     * For temporal value editing, provides the list of available offsets to choose from.
-     */
-    default List<ZoneOffset> getAvailableOffsets() {
-        val now = LocalDateTime.now();
-        return getAvailableZoneIds().stream()
-            .map(ZoneId::getRules)
-            .flatMap(zoneIdRules->zoneIdRules.getValidOffsets(now).stream())
-            .sorted()
-            .distinct()
-            .collect(Collectors.toList());
-    }
+    
 }
diff --git a/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/TimeZoneChoiceProvider.java b/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/TimeZoneChoiceProvider.java
new file mode 100644
index 0000000..9109deb
--- /dev/null
+++ b/api/applib/src/main/java/org/apache/causeway/applib/value/semantics/TimeZoneChoiceProvider.java
@@ -0,0 +1,68 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.causeway.applib.value.semantics;
+
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import lombok.val;
+
+/**
+ * When implemented by a service {@link #getAvailableZoneIds()} 
+ * can be customized to limit the time-zone choices 
+ * as e.g. offered by the <i>Wicket Viewer<i> login page.
+ * 
+ * @since 2.1, 3.1
+ */
+public interface TimeZoneChoiceProvider {
+    
+    /**
+     * For temporal value editing, provides the list of available time zones to choose from.
+     */
+    default List<ZoneId> getAvailableZoneIds() {
+        return ZoneId.getAvailableZoneIds().stream()
+            .sorted()
+            .map(ZoneId::of)
+            .collect(Collectors.toList());
+    }
+
+    /**
+     * For temporal value editing, provides the list of available offsets to choose from.
+     */
+    default List<ZoneOffset> getAvailableOffsets() {
+        val now = LocalDateTime.now();
+        return getAvailableZoneIds().stream()
+            .map(ZoneId::getRules)
+            .flatMap(zoneIdRules->zoneIdRules.getValidOffsets(now).stream())
+            .sorted()
+            .distinct()
+            .collect(Collectors.toList());
+    }
+    
+    /**
+     * Returns the fallback implementation, which provides all ZoneIds known to the JVM.
+     */
+    static TimeZoneChoiceProvider fallback() {
+        return new TimeZoneChoiceProvider() {};
+    }
+    
+}
diff --git a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/pages/login/SignInPanelAbstract.java b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/pages/login/SignInPanelAbstract.java
index 3cecaef..e779191 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/pages/login/SignInPanelAbstract.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/causeway/viewer/wicket/ui/pages/login/SignInPanelAbstract.java
@@ -37,9 +37,9 @@
 import org.apache.wicket.request.resource.ResourceReference;
 import org.apache.wicket.util.cookies.CookieUtils;
 
+import org.apache.causeway.applib.value.semantics.TimeZoneChoiceProvider;
 import org.apache.causeway.commons.internal.base._Strings;
 import org.apache.causeway.core.metamodel.context.HasMetaModelContext;
-import org.apache.causeway.core.metamodel.valuesemantics.temporal.ZonedDateTimeValueSemantics;
 import org.apache.causeway.viewer.wicket.ui.util.Wkt;
 
 import lombok.Getter;
@@ -266,7 +266,7 @@
 
             add(Wkt.dropDownChoice("timezone",
                     new PropertyModel<ZoneId>(SignInPanelAbstract.this, "timezone"),
-                    new ZonedDateTimeValueSemantics().getAvailableZoneIds())
+                    getTimeZoneChoiceProvider().getAvailableZoneIds())
                 .setRequired(true)
                 .setMarkupId(TIME_ZONE_SELECT));
 
@@ -309,6 +309,11 @@
             }
         }
 
+        private TimeZoneChoiceProvider getTimeZoneChoiceProvider() {
+            return getMetaModelContext().getServiceRegistry().lookupService(TimeZoneChoiceProvider.class)
+                    .orElseGet(TimeZoneChoiceProvider::fallback);
+        }
+        
     }
 
     /**