You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2021/09/09 06:19:31 UTC

[isis] branch master updated: ISIS-2869: simplify CalenderEvent:

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/master by this push:
     new 967c108  ISIS-2869: simplify CalenderEvent:
967c108 is described below

commit 967c108f22f945e02073ba78a2f5a0ca9d21c7ab
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Sep 9 08:19:23 2021 +0200

    ISIS-2869: simplify CalenderEvent:
    
    store the instant in time as epochMillis (long)
    
    its up to the client/caller to specify a time-zone
    
    also introducing a new maven artifact 'valuetypes-jodatime' designated
    to gather all the joda-time stuff
---
 .../isis/applib/jaxb/JodaTimeJaxbAdapters.java     |   4 +-
 .../services/iactnlayer/InteractionContext.java    |  14 +--
 .../services/wrapper/control/AsyncControl.java     |  12 +-
 .../fullcalendar/applib/value/CalendarEvent.java   | 100 +++++-----------
 extensions/vw/fullcalendar/ui/pom.xml              |   6 +
 .../ui/component/EventProviderAbstract.java        |  16 ++-
 valuetypes/jodatime/applib/pom.xml                 |  49 ++++++++
 .../applib/IsisModuleValJodatimeApplib.java        |  28 +++++
 .../jodatime/applib/jaxb/JodatimeJaxbAdapters.java |   9 +-
 .../jodatime/applib/value/JodatimeConverters.java  | 128 +++++++++++++++++++++
 valuetypes/jodatime/pom.xml                        |  33 ++++++
 valuetypes/pom.xml                                 |   7 ++
 12 files changed, 314 insertions(+), 92 deletions(-)

diff --git a/api/applib/src/main/java/org/apache/isis/applib/jaxb/JodaTimeJaxbAdapters.java b/api/applib/src/main/java/org/apache/isis/applib/jaxb/JodaTimeJaxbAdapters.java
index 8801bec..f6ecf13 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/jaxb/JodaTimeJaxbAdapters.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/jaxb/JodaTimeJaxbAdapters.java
@@ -32,8 +32,10 @@ import lombok.experimental.UtilityClass;
 
 /**
  * @since 2.0 {@index}
+ * @deprecated use org.apache.isis.valuetypes.jodatime.applib.jaxb.JodatimeJaxbAdapters instead
  */
 @UtilityClass
+@Deprecated(forRemoval = true, since = "2.0.0-M7")
 public final class JodaTimeJaxbAdapters {
 
     public static class LocalDateToStringAdapter extends XmlAdapter<String, LocalDate> {
@@ -164,7 +166,7 @@ public final class JodaTimeJaxbAdapters {
     }
 
 
-    private static boolean isNullOrEmpty(String x) {
+    private static boolean isNullOrEmpty(final String x) {
         return x == null || x.isEmpty();
     }
 
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContext.java b/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContext.java
index 9279a2c..d6eace9 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContext.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/iactnlayer/InteractionContext.java
@@ -19,8 +19,8 @@
 package org.apache.isis.applib.services.iactnlayer;
 
 import java.io.Serializable;
+import java.time.ZoneId;
 import java.util.Locale;
-import java.util.TimeZone;
 import java.util.function.UnaryOperator;
 import java.util.stream.Stream;
 
@@ -62,7 +62,7 @@ public class InteractionContext implements Serializable {
                 .user(user)
                 .clock(VirtualClock.system())
                 .locale(Locale.getDefault())
-                .timeZone(TimeZone.getDefault())
+                .timeZone(ZoneId.systemDefault())
                 .build();
     }
 
@@ -91,7 +91,7 @@ public class InteractionContext implements Serializable {
     final @NonNull Locale locale = Locale.getDefault();
 
     @With @Getter @Builder.Default
-    final @NonNull TimeZone timeZone = TimeZone.getDefault();
+    final @NonNull ZoneId timeZone = ZoneId.systemDefault();
 
 
     /**
@@ -124,14 +124,14 @@ public class InteractionContext implements Serializable {
     /**
      * Convenience method for use with {@link org.apache.isis.applib.services.sudo.SudoService}, returning a
      * {@link UnaryOperator} that will act upon the provided {@link InteractionContext} to return the same but with
-     * the specified {@link TimeZone}.
+     * the specified {@link ZoneId}.
      */
-    public static UnaryOperator<InteractionContext> switchTimeZone(final @NonNull TimeZone timeZone) {
+    public static UnaryOperator<InteractionContext> switchTimeZone(final @NonNull ZoneId timeZone) {
         return interactionContext -> interactionContext.withTimeZone(timeZone);
     }
 
     /**
-     * Convenience method to combine {@link UnaryOperator}s, for example as per {@link #switchUser(UserMemento)} and {@link #switchTimeZone(TimeZone)}.
+     * Convenience method to combine {@link UnaryOperator}s, for example as per {@link #switchUser(UserMemento)} and {@link #switchTimeZone(ZoneId)}.
      *
      * <p>
      * NOTE: this implementation can result in heap pollution; better to use the {@link #combine(Stream) overload}.
@@ -145,7 +145,7 @@ public class InteractionContext implements Serializable {
     }
 
     /**
-     * Convenience method to combine {@link UnaryOperator}s, for example as per {@link #switchUser(UserMemento)} and {@link #switchTimeZone(TimeZone)}.
+     * Convenience method to combine {@link UnaryOperator}s, for example as per {@link #switchUser(UserMemento)} and {@link #switchTimeZone(ZoneId)}.
      *
      * credit: https://stackoverflow.com/a/51065029/56880
      */
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/AsyncControl.java b/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/AsyncControl.java
index badfd21..bb80c02 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/AsyncControl.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/AsyncControl.java
@@ -18,8 +18,8 @@
  */
 package org.apache.isis.applib.services.wrapper.control;
 
+import java.time.ZoneId;
 import java.util.Locale;
-import java.util.TimeZone;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ForkJoinPool;
 import java.util.concurrent.Future;
@@ -101,7 +101,7 @@ public class AsyncControl<R> extends ControlAbstract<AsyncControl<R>> {
      * </p>
      */
     @Override
-    public AsyncControl with(ExceptionHandler exceptionHandler) {
+    public AsyncControl with(final ExceptionHandler exceptionHandler) {
         return super.with(exceptionHandler);
     }
 
@@ -122,7 +122,7 @@ public class AsyncControl<R> extends ControlAbstract<AsyncControl<R>> {
      *
      * @param executorService
      */
-    public AsyncControl<R> with(ExecutorService executorService) {
+    public AsyncControl<R> with(final ExecutorService executorService) {
         this.executorService = executorService;
         return this;
         // ...
@@ -154,8 +154,8 @@ public class AsyncControl<R> extends ControlAbstract<AsyncControl<R>> {
      * Defaults to the system time zone, if not overridden
      */
     @Getter
-    private TimeZone timeZone;
-    public AsyncControl<R> withTimeZone(final @NonNull TimeZone timeZone) {
+    private ZoneId timeZone;
+    public AsyncControl<R> withTimeZone(final @NonNull ZoneId timeZone) {
         this.timeZone = timeZone;
         return this;
         // ...
@@ -194,7 +194,7 @@ public class AsyncControl<R> extends ControlAbstract<AsyncControl<R>> {
     /**
      * For framework use only.
      */
-    public void setFuture(Future<R> future) {
+    public void setFuture(final Future<R> future) {
         this.future = future;
     }
 
diff --git a/extensions/vw/fullcalendar/applib/src/main/java/org/apache/isis/extensions/fullcalendar/applib/value/CalendarEvent.java b/extensions/vw/fullcalendar/applib/src/main/java/org/apache/isis/extensions/fullcalendar/applib/value/CalendarEvent.java
index 92f2970..6ce1b85 100644
--- a/extensions/vw/fullcalendar/applib/src/main/java/org/apache/isis/extensions/fullcalendar/applib/value/CalendarEvent.java
+++ b/extensions/vw/fullcalendar/applib/src/main/java/org/apache/isis/extensions/fullcalendar/applib/value/CalendarEvent.java
@@ -19,95 +19,53 @@
 package org.apache.isis.extensions.fullcalendar.applib.value;
 
 import java.io.Serializable;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
 
-import org.joda.time.DateTime;
+import org.springframework.lang.Nullable;
 
 import org.apache.isis.applib.annotation.Value;
-import org.apache.isis.applib.util.ObjectContracts;
-import org.apache.isis.applib.util.ObjectContracts.ObjectContract;
+
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.ToString;
+import lombok.With;
 
 /**
  * Value type representing an event on a calendar.
- *
  * @since 2.0 {@index}
+ * @apiNote implements Comparable<CalendarEvent> based on epochMillis
  */
 @Value(semanticsProviderClass=CalendarEventSemanticsProvider.class)
-public class CalendarEvent implements Serializable {
+@Getter @With
+@ToString @EqualsAndHashCode
+@AllArgsConstructor
+public class CalendarEvent
+implements
+    Comparable<CalendarEvent>,
+    Serializable {
 
     private static final long serialVersionUID = 1L;
 
-	static final CalendarEvent DEFAULT_VALUE = null; // no default
-
-    private final DateTime dateTime;
-    private final String calendarName;
-    private final String title;
-    private final String notes;
-
-	public CalendarEvent(final DateTime dateTime, final String calendarName, final String title) {
-        this(dateTime, calendarName, title, null);
-	}
-
-    public CalendarEvent(final DateTime dateTime, final String calendarName, final String title, final String notes) {
-        this.dateTime = dateTime;
-        this.calendarName = calendarName;
-        this.title = title;
-        this.notes = notes;
-    }
+    private final long epochMillis;
+    private final @NonNull String calendarName;
+    private final @NonNull String title;
+    private final @Nullable String notes;
 
-    public DateTime getDateTime() {
-        return dateTime;
+    public ZonedDateTime asDateTime(final ZoneId zoneId) {
+        return ZonedDateTime.ofInstant(Instant.ofEpochMilli(epochMillis), zoneId);
     }
 
-    public String getCalendarName() {
-        return calendarName;
-    }
-
-    public String getTitle() {
-        return title;
-    }
-
-    public String getNotes() {
-        return notes;
-    }
-
-	public CalendarEvent withDateTime(final DateTime date) {
-		return new CalendarEvent(date, this.calendarName, this.title, this.notes);
-	}
-
-	public CalendarEvent withCalendarName(final String calendarName) {
-	    return new CalendarEvent(this.dateTime, calendarName, this.title, this.notes);
-	}
-
-	public CalendarEvent withTitle(final String title) {
-	    return new CalendarEvent(this.dateTime, this.calendarName, title, this.notes);
-	}
-
-	public CalendarEvent withNotes(final String notes) {
-	    return new CalendarEvent(this.dateTime, this.calendarName, this.title, notes);
-	}
-
-	private static final ObjectContract<CalendarEvent> objectContract =
-	    ObjectContracts.contract(CalendarEvent.class)
-	    .thenUse("dateTime", CalendarEvent::getDateTime)
-	    .thenUse("calendarName", CalendarEvent::getCalendarName);
-
-
-	@Override
-    public int hashCode() {
-	    return objectContract.hashCode(this);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        return objectContract.equals(this, obj);
+    public ZonedDateTime asDateTime() {
+        return asDateTime(ZoneId.systemDefault());
     }
 
     @Override
-    public String toString() {
-        return objectContract.toString(this);
+    public int compareTo(final CalendarEvent other) {
+        return Long.compare(this.epochMillis, other.getEpochMillis());
     }
 
-    public static int typicalLength() {
-		return 30;
-	}
 }
diff --git a/extensions/vw/fullcalendar/ui/pom.xml b/extensions/vw/fullcalendar/ui/pom.xml
index 09345c3..50caf9b 100644
--- a/extensions/vw/fullcalendar/ui/pom.xml
+++ b/extensions/vw/fullcalendar/ui/pom.xml
@@ -57,6 +57,12 @@
             <groupId>org.apache.isis.extensions</groupId>
             <artifactId>isis-extensions-fullcalendar-applib</artifactId>
         </dependency>
+        
+        <dependency>
+			<groupId>org.apache.isis.valuetypes</groupId>
+			<artifactId>isis-valuetypes-jodatime-applib</artifactId>
+			<version>2.0.0-SNAPSHOT</version>
+		</dependency>
 
         <dependency>
             <groupId>org.apache.isis.viewer</groupId>
diff --git a/extensions/vw/fullcalendar/ui/src/main/java/org/apache/isis/extensions/fullcalendar/ui/component/EventProviderAbstract.java b/extensions/vw/fullcalendar/ui/src/main/java/org/apache/isis/extensions/fullcalendar/ui/component/EventProviderAbstract.java
index c0420f1..4c71c9a 100644
--- a/extensions/vw/fullcalendar/ui/src/main/java/org/apache/isis/extensions/fullcalendar/ui/component/EventProviderAbstract.java
+++ b/extensions/vw/fullcalendar/ui/src/main/java/org/apache/isis/extensions/fullcalendar/ui/component/EventProviderAbstract.java
@@ -18,6 +18,7 @@
  */
 package org.apache.isis.extensions.fullcalendar.ui.component;
 
+import java.time.ZoneId;
 import java.util.Collection;
 import java.util.Map;
 import java.util.Objects;
@@ -28,6 +29,7 @@ import java.util.stream.Collectors;
 import org.joda.time.DateTime;
 import org.joda.time.Interval;
 
+import org.apache.isis.applib.services.iactnlayer.InteractionContext;
 import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.commons.internal.collections._Maps;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
@@ -35,6 +37,7 @@ import org.apache.isis.core.metamodel.spec.ManagedObjects;
 import org.apache.isis.core.runtime.context.IsisAppCommonContext;
 import org.apache.isis.extensions.fullcalendar.applib.spi.CalendarableDereferencingService;
 import org.apache.isis.extensions.fullcalendar.applib.value.CalendarEvent;
+import org.apache.isis.valuetypes.jodatime.applib.value.JodatimeConverters;
 import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
 
 import lombok.val;
@@ -90,10 +93,15 @@ public abstract class EventProviderAbstract implements EventProvider {
                 return null;
             }
 
-            final Event event = new Event();
+            val timeZone = commonContext.getInteractionProvider()
+                    .currentInteractionContext()
+                    .map(InteractionContext::getTimeZone)
+                    .orElse(ZoneId.systemDefault());
+
+            val start = JodatimeConverters.toJoda(calendarEvent.asDateTime(timeZone));
+            val end = start;
 
-            final DateTime start = calendarEvent.getDateTime();
-            final DateTime end = start;
+            final Event event = new Event();
             event.setStart(start);
             event.setEnd(end);
             event.setAllDay(true);
@@ -143,7 +151,7 @@ public abstract class EventProviderAbstract implements EventProvider {
     }
 
     @Override
-    public Event getEventForId(String id) throws EventNotFoundException {
+    public Event getEventForId(final String id) throws EventNotFoundException {
         return eventById.get(id);
     }
 
diff --git a/valuetypes/jodatime/applib/pom.xml b/valuetypes/jodatime/applib/pom.xml
new file mode 100644
index 0000000..1941555
--- /dev/null
+++ b/valuetypes/jodatime/applib/pom.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<parent>
+		<groupId>org.apache.isis.valuetypes</groupId>
+		<artifactId>isis-valuetypes-jodatime</artifactId>
+		<version>2.0.0-SNAPSHOT</version>
+	</parent>
+
+	<artifactId>isis-valuetypes-jodatime-applib</artifactId>
+	<name>Apache Isis Val - Joda Time (applib)</name>
+
+	<properties>
+		<jar-plugin.automaticModuleName>org.apache.isis.valuetypes.jodatime.applib</jar-plugin.automaticModuleName>
+		<git-plugin.propertiesDir>org/apache/isis/valuetypes/jodatime/applib</git-plugin.propertiesDir>
+	</properties>
+
+	<dependencies>
+
+		<dependency>
+			<groupId>org.apache.isis.core</groupId>
+			<artifactId>isis-applib</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.isis.commons</groupId>
+			<artifactId>isis-commons</artifactId>
+		</dependency>
+
+		<dependency>
+			<groupId>joda-time</groupId>
+			<artifactId>joda-time</artifactId>
+		</dependency>
+
+	</dependencies>
+
+</project>
diff --git a/valuetypes/jodatime/applib/src/main/java/org/apache/isis/valuetypes/jodatime/applib/IsisModuleValJodatimeApplib.java b/valuetypes/jodatime/applib/src/main/java/org/apache/isis/valuetypes/jodatime/applib/IsisModuleValJodatimeApplib.java
new file mode 100644
index 0000000..288bee6
--- /dev/null
+++ b/valuetypes/jodatime/applib/src/main/java/org/apache/isis/valuetypes/jodatime/applib/IsisModuleValJodatimeApplib.java
@@ -0,0 +1,28 @@
+/*
+ *  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.isis.valuetypes.jodatime.applib;
+
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @since 2.0 {@index}
+ */
+@Configuration
+public class IsisModuleValJodatimeApplib {
+}
diff --git a/api/applib/src/main/java/org/apache/isis/applib/jaxb/JodaTimeJaxbAdapters.java b/valuetypes/jodatime/applib/src/main/java/org/apache/isis/valuetypes/jodatime/applib/jaxb/JodatimeJaxbAdapters.java
similarity index 96%
copy from api/applib/src/main/java/org/apache/isis/applib/jaxb/JodaTimeJaxbAdapters.java
copy to valuetypes/jodatime/applib/src/main/java/org/apache/isis/valuetypes/jodatime/applib/jaxb/JodatimeJaxbAdapters.java
index 8801bec..880d133 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/jaxb/JodaTimeJaxbAdapters.java
+++ b/valuetypes/jodatime/applib/src/main/java/org/apache/isis/valuetypes/jodatime/applib/jaxb/JodatimeJaxbAdapters.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.applib.jaxb;
+package org.apache.isis.valuetypes.jodatime.applib.jaxb;
 
 import javax.xml.bind.annotation.adapters.XmlAdapter;
 import javax.xml.datatype.XMLGregorianCalendar;
@@ -28,13 +28,15 @@ import org.joda.time.LocalTime;
 import org.joda.time.format.DateTimeFormatter;
 import org.joda.time.format.ISODateTimeFormat;
 
+import org.apache.isis.applib.jaxb.JodaTimeXMLGregorianCalendarMarshalling;
+
 import lombok.experimental.UtilityClass;
 
 /**
  * @since 2.0 {@index}
  */
 @UtilityClass
-public final class JodaTimeJaxbAdapters {
+public final class JodatimeJaxbAdapters {
 
     public static class LocalDateToStringAdapter extends XmlAdapter<String, LocalDate> {
 
@@ -164,9 +166,10 @@ public final class JodaTimeJaxbAdapters {
     }
 
 
-    private static boolean isNullOrEmpty(String x) {
+    private static boolean isNullOrEmpty(final String x) {
         return x == null || x.isEmpty();
     }
 
 
 }
+
diff --git a/valuetypes/jodatime/applib/src/main/java/org/apache/isis/valuetypes/jodatime/applib/value/JodatimeConverters.java b/valuetypes/jodatime/applib/src/main/java/org/apache/isis/valuetypes/jodatime/applib/value/JodatimeConverters.java
new file mode 100644
index 0000000..e3fba9d
--- /dev/null
+++ b/valuetypes/jodatime/applib/src/main/java/org/apache/isis/valuetypes/jodatime/applib/value/JodatimeConverters.java
@@ -0,0 +1,128 @@
+/*
+ *  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.isis.valuetypes.jodatime.applib.value;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.Instant;
+import org.joda.time.LocalDate;
+import org.joda.time.LocalDateTime;
+import org.joda.time.LocalTime;
+
+import lombok.experimental.UtilityClass;
+
+/**
+ * @since 2.0 {@index}
+ */
+@UtilityClass
+public final class JodatimeConverters {
+
+    // LOCAL TIME
+
+    public Instant toJoda(final java.time.Instant input) {
+        return new Instant(input.toEpochMilli());
+    }
+
+    public java.time.Instant fromJoda(final Instant input) {
+        return java.time.Instant.ofEpochMilli(input.getMillis());
+    }
+
+    // LOCAL TIME
+
+    public LocalTime toJoda(final java.time.LocalTime input) {
+        return new LocalTime(
+                input.getHour(), input.getMinute(), input.getSecond(),
+                nanosToMillis(input.getNano()));
+    }
+
+    public java.time.LocalTime fromJoda(final LocalTime input) {
+        return java.time.LocalTime.of(
+                input.getHourOfDay(), input.getMinuteOfHour(), input.getSecondOfMinute(),
+                millisToNanos(input.getMillisOfSecond()));
+    }
+
+    // LOCAL DATE
+
+    public LocalDate toJoda(final java.time.LocalDate input) {
+        return new LocalDate(
+                input.getYear(), input.getMonthValue(), input.getDayOfMonth());
+    }
+
+    public java.time.LocalDate fromJoda(final LocalDate input) {
+        return java.time.LocalDate.of(
+                input.getYear(), input.getMonthOfYear(), input.getDayOfMonth());
+    }
+
+    // LOCAL DATE TIME
+
+    public LocalDateTime toJoda(final java.time.LocalDateTime input) {
+        return new LocalDateTime(
+                input.getYear(), input.getMonthValue(), input.getDayOfMonth(),
+                input.getHour(), input.getMinute(), input.getSecond(),
+                nanosToMillis(input.getNano()));
+    }
+
+    public java.time.LocalDateTime fromJoda(final LocalDateTime input) {
+        return java.time.LocalDateTime.of(
+                input.getYear(), input.getMonthOfYear(), input.getDayOfMonth(),
+                input.getHourOfDay(), input.getMinuteOfHour(), input.getSecondOfMinute(),
+                millisToNanos(input.getMillisOfSecond()));
+    }
+
+    // DATE TIME WITH TIME ZONE DATA
+
+    public DateTime toJoda(final java.time.ZonedDateTime input) {
+        return new DateTime(
+                input.getYear(), input.getMonthValue(), input.getDayOfMonth(),
+                input.getHour(), input.getMinute(), input.getSecond(),
+                nanosToMillis(input.getNano()),
+                toJoda(input.getZone()));
+    }
+
+    public java.time.ZonedDateTime fromJoda(final DateTime input) {
+        return java.time.ZonedDateTime.of(
+                input.getYear(), input.getMonthOfYear(), input.getDayOfMonth(),
+                input.getHourOfDay(), input.getMinuteOfHour(), input.getSecondOfMinute(),
+                millisToNanos(input.getMillisOfSecond()),
+                fromJoda(input.getZone()));
+    }
+
+    // TIME ZONE
+
+    public DateTimeZone toJoda(final java.time.ZoneId input) {
+        return DateTimeZone.forID(input.getId());
+    }
+
+    public java.time.ZoneId fromJoda(final DateTimeZone input) {
+        return java.time.ZoneId.of(input.getID());
+    }
+
+    // -- HELPER
+
+    // private, as we don't, check any overflows or negative values
+    private int nanosToMillis(final int nanos) {
+        return nanos/1000_000;
+    }
+
+    // private, as we don't, check any overflows or negative values
+    private int millisToNanos(final int millis) {
+        return 1000_000 * millis;
+    }
+
+}
diff --git a/valuetypes/jodatime/pom.xml b/valuetypes/jodatime/pom.xml
new file mode 100644
index 0000000..f90a2f1
--- /dev/null
+++ b/valuetypes/jodatime/pom.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<parent>
+		<groupId>org.apache.isis.valuetypes</groupId>
+		<artifactId>isis-valuetypes</artifactId>
+		<version>2.0.0-SNAPSHOT</version>
+	</parent>
+
+	<artifactId>isis-valuetypes-jodatime</artifactId>
+	<name>Apache Isis Val - Joda Time (parent)</name>
+	<description>Joda Time Library</description>
+
+	<packaging>pom</packaging>
+
+	<modules>
+		<module>applib</module>
+	</modules>
+
+</project>
diff --git a/valuetypes/pom.xml b/valuetypes/pom.xml
index bc1f766..994c42b 100644
--- a/valuetypes/pom.xml
+++ b/valuetypes/pom.xml
@@ -63,6 +63,12 @@
 
 			<dependency>
 				<groupId>org.apache.isis.valuetypes</groupId>
+				<artifactId>isis-valuetypes-jodatime-applib</artifactId>
+				<version>2.0.0-SNAPSHOT</version>
+			</dependency>
+
+			<dependency>
+				<groupId>org.apache.isis.valuetypes</groupId>
 				<artifactId>isis-valuetypes-asciidoc-applib</artifactId>
 				<version>2.0.0-SNAPSHOT</version>
 			</dependency>
@@ -154,6 +160,7 @@
 	</dependencies>
 
 	<modules>
+		<module>jodatime</module>
 		<module>asciidoc</module>
 		<module>markdown</module>
 		<module>sse</module>