[jackson-datatype-joda] 01/11: Imported Upstream version 2.4.3

Tim Potter tpot-guest at moszumanska.debian.org
Thu Nov 6 00:19:57 UTC 2014


This is an automated email from the git hooks/post-receive script.

tpot-guest pushed a commit to branch master
in repository jackson-datatype-joda.

commit 0244e0231f0b25399544280c04f6bb205c81a31e
Author: Tim Potter <tpot at hp.com>
Date:   Tue Nov 4 11:06:56 2014 +1100

    Imported Upstream version 2.4.3
---
 .gitignore                                         |  22 +
 README.md                                          |  70 +++
 pom.xml                                            |  90 ++++
 release-notes/CREDITS                              |  26 ++
 release-notes/VERSION                              | 112 +++++
 .../jackson/datatype/joda/JodaMapper.java          |  33 ++
 .../jackson/datatype/joda/JodaModule.java          |  70 +++
 .../jackson/datatype/joda/PackageVersion.java.in   |  20 +
 .../joda/deser/DateMidnightDeserializer.java       |  58 +++
 .../datatype/joda/deser/DateTimeDeserializer.java  |  64 +++
 .../joda/deser/DateTimeZoneDeserializer.java       |  37 ++
 .../datatype/joda/deser/DurationDeserializer.java  |  37 ++
 .../datatype/joda/deser/InstantDeserializer.java   |  44 ++
 .../datatype/joda/deser/IntervalDeserializer.java  |  46 ++
 .../datatype/joda/deser/JodaDeserializerBase.java  |  21 +
 .../datatype/joda/deser/LocalDateDeserializer.java |  53 +++
 .../joda/deser/LocalDateTimeDeserializer.java      |  69 +++
 .../datatype/joda/deser/LocalTimeDeserializer.java |  59 +++
 .../datatype/joda/deser/MonthDayDeserializer.java  |  42 ++
 .../datatype/joda/deser/PeriodDeserializer.java    |  34 ++
 .../joda/deser/ReadablePeriodDeserializer.java     |  53 +++
 .../datatype/joda/deser/YearMonthDeserializer.java |  42 ++
 .../joda/deser/key/DateTimeKeyDeserializer.java    |  17 +
 .../joda/deser/key/JodaKeyDeserializer.java        |  21 +
 .../joda/deser/key/LocalDateKeyDeserializer.java   |  18 +
 .../deser/key/LocalDateTimeKeyDeserializer.java    |  18 +
 .../joda/deser/key/LocalTimeKeyDeserializer.java   |  18 +
 .../datatype/joda/ser/DateMidnightSerializer.java  |  48 ++
 .../datatype/joda/ser/DateTimeSerializer.java      |  45 ++
 .../datatype/joda/ser/DateTimeZoneSerializer.java  |  21 +
 .../datatype/joda/ser/DurationSerializer.java      |  46 ++
 .../datatype/joda/ser/InstantSerializer.java       |  42 ++
 .../datatype/joda/ser/IntervalSerializer.java      |  24 +
 .../datatype/joda/ser/JacksonJodaFormat.java       | 184 ++++++++
 .../datatype/joda/ser/JodaDateSerializerBase.java  |  78 ++++
 .../datatype/joda/ser/JodaSerializerBase.java      |  21 +
 .../datatype/joda/ser/LocalDateSerializer.java     |  48 ++
 .../datatype/joda/ser/LocalDateTimeSerializer.java |  52 +++
 .../datatype/joda/ser/LocalTimeSerializer.java     |  49 ++
 .../datatype/joda/ser/PeriodSerializer.java        |  37 ++
 src/main/resources/META-INF/LICENSE                |   8 +
 .../services/com.fasterxml.jackson.databind.Module |   1 +
 .../jackson/datatype/joda/DateTimeTest.java        | 100 ++++
 .../jackson/datatype/joda/JodaMapperTest.java      |  32 ++
 .../datatype/joda/JodaSerializationTest.java       | 246 ++++++++++
 .../jackson/datatype/joda/JodaTestBase.java        |  46 ++
 .../jackson/datatype/joda/MixedListTest.java       |  42 ++
 .../jackson/datatype/joda/TestVersions.java        |  29 ++
 .../joda/deser/MiscDeserializationTest.java        | 516 +++++++++++++++++++++
 .../joda/deser/ReadablePeriodDeserializerTest.java |  73 +++
 50 files changed, 2982 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c3bde80
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,22 @@
+# use glob syntax.
+syntax: glob
+*.class
+*~
+*.bak
+*.off
+*.old
+.DS_Store
+
+# building
+target
+
+# Eclipse
+.classpath
+.project
+.settings
+
+# IDEA
+*.iml
+*.ipr
+*.iws
+.idea
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d423888
--- /dev/null
+++ b/README.md
@@ -0,0 +1,70 @@
+Project to build [Jackson](http://jackson.codehaus.org) module (jar)
+to support JSON serialization and deserialization of
+[Joda](http://joda-time.sourceforge.net/) data types.
+
+## Status
+
+[![Build Status](https://fasterxml.ci.cloudbees.com/job/jackson-datatype-joda-master/badge/icon)](https://fasterxml.ci.cloudbees.com/job/jackson-datatype-joda-master/)
+
+As of version 2.0 module is usable and relatively extensive.
+Contributions are always welcome -- not all types are yet supported; and we may want to support even wider alternative
+formats on input side.
+
+## Usage
+
+Since this module extends basic [Jackson databind](../../../jackson-databind) functionality, you may want to check out
+documentation at [Jackson-docs](../../../jackson-docs) first.
+
+### Maven dependency
+
+To use module on Maven-based projects, use following dependency:
+
+```xml
+<dependency>
+  <groupId>com.fasterxml.jackson.datatype</groupId>
+  <artifactId>jackson-datatype-joda</artifactId>
+  <version>2.4.0</version>
+</dependency>    
+```
+
+(or whatever version is most up-to-date at the moment)
+
+### Registering module
+
+To use Joda datatypes with Jackson, you will first need to register the module first (same as
+with all Jackson datatype modules):
+
+```java
+ObjectMapper mapper = new ObjectMapper();
+mapper.registerModule(new JodaModule());
+```
+
+### Reading and Writing Joda types
+
+After registering Joda module, [Jackson Databind](../../../jackson-databind) will be able to write values
+of supported Joda types as JSON (and other formats Jackson supports), and read Joda values
+from same formats.
+
+With JSON, for example, following would work
+
+```java
+public class Bean {
+  public DateTime start;
+}
+
+final String INPUT_JSON = "{\"start\" : \"1972-12-28T12:00:01.000Z\"}";
+Bean bean = mapper.readValue(INPUT_JSON, Bean.class);
+```
+
+and property `start` of Bean would have expected `DateTime` value.
+
+Conversely, you can produce JSON (and other supported formats) simply with:
+
+```java
+String json = mapper.writeValueAsString(bean);
+Assert.assertEquals(INPUT_JSON, json);
+```
+
+## More
+
+See [Wiki](../../wiki) for more information (javadocs, downloads).
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..5cdfa22
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion> 
+  <parent>
+    <groupId>com.fasterxml.jackson</groupId>
+    <artifactId>jackson-parent</artifactId>
+    <version>2.4</version>
+  </parent>
+  <groupId>com.fasterxml.jackson.datatype</groupId>
+  <artifactId>jackson-datatype-joda</artifactId>
+  <name>Jackson-datatype-Joda</name>
+  <version>2.4.3</version>
+  <packaging>bundle</packaging>
+  <description>Add-on module for Jackson (http://jackson.codehaus.org) to support
+Joda (http://joda-time.sourceforge.net/) data types.
+  </description>
+  <url>http://wiki.fasterxml.com/JacksonModuleJoda</url>
+  <scm>
+    <connection>scm:git:git at github.com:FasterXML/jackson-datatype-joda.git</connection>
+    <developerConnection>scm:git:git at github.com:FasterXML/jackson-datatype-joda.git</developerConnection>
+    <url>http://github.com/FasterXML/jackson-datatype-joda</url>    
+    <tag>jackson-datatype-joda-2.4.3</tag>
+  </scm>
+  <properties>
+    <version.jackson.annotations>2.4.0</version.jackson.annotations>
+    <version.jackson.core>2.4.3</version.jackson.core>
+    <!-- Generate PackageVersion.java into this directory. -->
+    <packageVersion.dir>com/fasterxml/jackson/datatype/joda</packageVersion.dir>
+    <packageVersion.package>${project.groupId}.joda</packageVersion.package>
+
+    <!-- Configuration properties for the OSGi maven-bundle-plugin -->
+    <osgi.export>${project.groupId}.joda.*</osgi.export>
+    <osgi.versionpolicy>${range;[===,+);${@}}</osgi.versionpolicy>
+  </properties>
+
+  <dependencies>
+    <!-- Extends Jackson mapper; but also needs annotations slightly,
+         to support JsonFormat
+      -->
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-annotations</artifactId>
+      <version>${version.jackson.annotations}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-core</artifactId>
+      <version>${version.jackson.core}</version>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
+      <version>${version.jackson.core}</version>
+    </dependency>
+
+    <!-- And obviously also depends on Joda lib -->
+    <dependency>
+        <groupId>joda-time</groupId>
+        <artifactId>joda-time</artifactId>
+        <!-- Upgraded to 2.2 for Jackson 2.4: but let's try not to call any 2.2 methods
+          -->
+        <version>2.2</version>
+    </dependency>
+<!-- 26-Jun-2012, tatu: Not sure if this should be included; it appears to be
+   a transitive dependency. Leaving out for now.
+    <dependency>
+        <groupId>org.joda</groupId>
+        <artifactId>joda-convert</artifactId>
+        <version>1.2</version>
+    </dependency>
+-->
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <!-- Inherited from oss-base. Generate PackageVersion.java.-->
+        <groupId>com.google.code.maven-replacer-plugin</groupId>
+        <artifactId>replacer</artifactId>
+        <executions>
+          <execution>
+            <id>process-packageVersion</id>
+            <phase>generate-sources</phase>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/release-notes/CREDITS b/release-notes/CREDITS
new file mode 100644
index 0000000..eae5873
--- /dev/null
+++ b/release-notes/CREDITS
@@ -0,0 +1,26 @@
+Here are people who have contributed to the development of Jackson JSON processor
+Joda datatype module.
+(version numbers in brackets indicate release in which the problem was fixed)
+
+Tatu Saloranta, tatu.saloranta at iki.fi: author
+
+ncjones at github.com:
+ * Contributed #19:  Add support for `MonthDay` and `YearMonth`
+  (2.3.1)
+
+Łukasz D:
+ * Suggested #23: package as a bundle
+  (2.3.1)
+
+Lorcan C
+ * Contributed #25: Add `KeyDeserializer` for `DateTime`
+  (2.3.1)
+
+Hendy Irawan (ceefour at github)
+ * Contributed #27: Allow support for `DateTimeZone`
+  (2.3.1)
+
+Brad Kennedy (bkenned4 at github)
+ * Contributed #45: Can't use LocalTime, LocalDate & LocalDateTime as Key type for a Map
+  (2.4.3)
+
diff --git a/release-notes/VERSION b/release-notes/VERSION
new file mode 100644
index 0000000..3dad9bd
--- /dev/null
+++ b/release-notes/VERSION
@@ -0,0 +1,112 @@
+Project: jackson-datatype-joda
+Version: 2.4.3 (04-Oct-2014)
+
+#45: Can't use LocalTime, LocalDate & LocalDateTime as Key type for a Map
+ (contributed by Brad K, reported by Guido M)
+#46: Interval deserialization fails for negative start instants
+ (reported by Dan G, dgrabows at github)
+
+------------------------------------------------------------------------
+=== History: ===
+------------------------------------------------------------------------
+
+2.4.2 (15-Aug-2014)
+
+No changes since 2.4.0
+
+2.4.1 (17-Jun-2014)
+
+No changes since 2.4.0
+
+2.4.0 (03-Jun-2014)
+
+#40: Add support for `ReadablePeriod`
+ (contributed by 'wimdeblauwe at github')
+- Joda dependency now to 2.2
+
+2.3.3 (14-Apr-2014)
+
+#32: Support use of `@JsonFormat(shape=...)` for timestamp/string choice
+
+2.3.2 (01-Mar-2014)
+
+#16: Adjust existing Date/Time deserializers to support `DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE`
+ (contributed by lukelukeluke at github)
+
+2.3.1 (28-Dec-2013)
+
+#19: Add serializers and deserializers for MonthDay and YearMonth
+ (contributed by ncjones at github)
+#21: Make `DateTimeSerializer` use configured timezone
+ (contributed by pavax at github)
+#23: Package as a bundle (was accidentally not, just bare jar)
+ (suggested by Łukasz D)
+#24: Allow serializing `Duration` using ISO-8601 notation
+ (requested by cowwoc at github)
+#25: Add `KeyDeserializer` for `DateTime`
+ (contributed by Lorcan C)
+#26: Implement `equals()` and `hashCode()` for JodaModule
+ (suggested by Henning S)
+#27: Add support for `DateTimeZone`
+ (requested by Hendy Irawan)
+
+2.3.0 (14-Nov-2013)
+
+#18: Add `JodaMapper`, sub-class of basic `ObjectMapper` that auto-registers
+  Joda module
+
+2.2.3 (25-Aug-2013)
+2.2.2 (27-May-2013)
+2.2.1 (04-May-2013)
+
+No functional changes.
+
+2.2.0 (23-Apr-2013)
+
+#8: Make DateTimeDeserializer abide by configured TimeZone
+- Upgraded version detection not to use VERSION.txt file
+
+2.1.2 (08-Dec-2012)
+2.1.1 (13-Nov-2012)
+
+No functional changes.
+
+2.1.0 (08-Oct-2012)
+
+New minor version, based on Jackson core 2.1.
+
+Improvements:
+
+- [Issue#9]: Add support for (de)serialiazing Interval
+ (submitted by jkolobok at github)
+
+2.0.4 (26-Jun-2012)
+
+Improvements:
+
+- [Issue-6] Add support for handling Local date types
+ (submitted by Chris Stivers)
+- Add support for Joda Instant data type
+
+2.0.3 (15-Jun-2012)
+
+Fixes:
+
+- [Issue-3] Add support for Duration serialization, deserialization
+ (reported by Marshall Pierce)
+
+2.0.2 (18-May-2012)
+
+No fixes, just syncing up with core releases.
+
+2.0.1 (29-Mar-2012)
+
+Fixes:
+
+* Issue-1: Deserializers registered so they would handle Object type,
+  messing up 'untyped' Lists, Maps
+ (reported by Pierre-Alexander M)
+
+2.0.0 (25-Mar-2012)
+
+The first official release
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/JodaMapper.java b/src/main/java/com/fasterxml/jackson/datatype/joda/JodaMapper.java
new file mode 100644
index 0000000..d5e4929
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/JodaMapper.java
@@ -0,0 +1,33 @@
+package com.fasterxml.jackson.datatype.joda;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+
+public class JodaMapper extends ObjectMapper
+{
+    private static final long serialVersionUID = 1L;
+
+    public JodaMapper() {
+        registerModule(new JodaModule());
+    }
+
+    /**
+     * Convenience method that is shortcut for:
+     *<pre>
+     *  module.isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+     *<pre>
+     */
+    public boolean getWriteDatesAsTimestamps() {
+        return getSerializationConfig().isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+    }
+
+    /**
+     * Convenience method that is shortcut for:
+     *<pre>
+     *  configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, state)
+     *<pre>
+     */
+    public void setWriteDatesAsTimestamps(boolean state) {
+        configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, state);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/JodaModule.java b/src/main/java/com/fasterxml/jackson/datatype/joda/JodaModule.java
new file mode 100644
index 0000000..91eca1c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/JodaModule.java
@@ -0,0 +1,70 @@
+package com.fasterxml.jackson.datatype.joda;
+
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.fasterxml.jackson.datatype.joda.deser.*;
+import com.fasterxml.jackson.datatype.joda.deser.key.*;
+import com.fasterxml.jackson.datatype.joda.ser.*;
+import org.joda.time.*;
+
+public class JodaModule extends SimpleModule
+{
+    private static final long serialVersionUID = 1L;
+
+    public JodaModule()
+    {
+        super(PackageVersion.VERSION);
+        // first deserializers
+        addDeserializer(DateMidnight.class, new DateMidnightDeserializer());
+        addDeserializer(DateTime.class, DateTimeDeserializer.forType(DateTime.class));
+        addDeserializer(DateTimeZone.class, new DateTimeZoneDeserializer());
+
+        addDeserializer(Duration.class, new DurationDeserializer());
+        addDeserializer(Instant.class, new InstantDeserializer());
+        addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
+        addDeserializer(LocalDate.class, new LocalDateDeserializer());
+        addDeserializer(LocalTime.class, new LocalTimeDeserializer());
+        addDeserializer(Period.class, new PeriodDeserializer());
+        addDeserializer(ReadableDateTime.class, DateTimeDeserializer.forType(ReadableDateTime.class));
+        addDeserializer(ReadableInstant.class, DateTimeDeserializer.forType(ReadableInstant.class));
+        addDeserializer(Interval.class, new IntervalDeserializer());
+        addDeserializer(MonthDay.class, new MonthDayDeserializer());
+        addDeserializer(YearMonth.class, new YearMonthDeserializer());
+
+		addDeserializer(ReadablePeriod.class, new ReadablePeriodDeserializer());
+
+        // then serializers:
+        final JsonSerializer<Object> stringSer = ToStringSerializer.instance;
+        addSerializer(DateMidnight.class, new DateMidnightSerializer());
+        addSerializer(DateTime.class, new DateTimeSerializer());
+        addSerializer(DateTimeZone.class, new DateTimeZoneSerializer());
+        addSerializer(Duration.class, new DurationSerializer());
+        addSerializer(Instant.class, new InstantSerializer());
+        addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
+        addSerializer(LocalDate.class, new LocalDateSerializer());
+        addSerializer(LocalTime.class, new LocalTimeSerializer());
+        addSerializer(Period.class, new PeriodSerializer());
+        addSerializer(Interval.class, new IntervalSerializer());
+        addSerializer(MonthDay.class, stringSer);
+        addSerializer(YearMonth.class, stringSer);
+
+        // then key deserializers
+        addKeyDeserializer(DateTime.class, new DateTimeKeyDeserializer());
+        addKeyDeserializer(LocalTime.class, new LocalTimeKeyDeserializer());
+        addKeyDeserializer(LocalDate.class, new LocalDateKeyDeserializer());
+        addKeyDeserializer(LocalDateTime.class, new LocalDateTimeKeyDeserializer());
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return getClass().hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        return this == o;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/PackageVersion.java.in b/src/main/java/com/fasterxml/jackson/datatype/joda/PackageVersion.java.in
new file mode 100644
index 0000000..7860aa1
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/PackageVersion.java.in
@@ -0,0 +1,20 @@
+package @package@;
+
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.core.Versioned;
+import com.fasterxml.jackson.core.util.VersionUtil;
+
+/**
+ * Automatically generated from PackageVersion.java.in during
+ * packageVersion-generate execution of maven-replacer-plugin in
+ * pom.xml.
+ */
+public final class PackageVersion implements Versioned {
+    public final static Version VERSION = VersionUtil.parseVersion(
+        "@projectversion@", "@projectgroupid@", "@projectartifactid@");
+
+    @Override
+    public Version version() {
+        return VERSION;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/DateMidnightDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/DateMidnightDeserializer.java
new file mode 100644
index 0000000..43ddce8
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/DateMidnightDeserializer.java
@@ -0,0 +1,58 @@
+package com.fasterxml.jackson.datatype.joda.deser;
+
+import java.io.IOException;
+
+import org.joda.time.DateMidnight;
+import org.joda.time.LocalDate;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+
+public class DateMidnightDeserializer
+    extends JodaDeserializerBase<DateMidnight>
+{
+    private static final long serialVersionUID = 1L;
+
+    final static DateTimeFormatter parser = ISODateTimeFormat.localDateParser();
+
+    public DateMidnightDeserializer() { super(DateMidnight.class); }
+
+    @Override
+    public DateMidnight deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // We'll accept either long (timestamp) or array:
+        if (jp.isExpectedStartArrayToken()) {
+            jp.nextToken(); // VALUE_NUMBER_INT 
+            int year = jp.getIntValue(); 
+            jp.nextToken(); // VALUE_NUMBER_INT
+            int month = jp.getIntValue();
+            jp.nextToken(); // VALUE_NUMBER_INT
+            int day = jp.getIntValue();
+            if (jp.nextToken() != JsonToken.END_ARRAY) {
+                throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY, "after DateMidnight ints");
+            }
+            return new DateMidnight(year, month, day);
+        }
+        switch (jp.getCurrentToken()) {
+        case VALUE_NUMBER_INT:
+            return new DateMidnight(jp.getLongValue());            
+        case VALUE_STRING:
+            String str = jp.getText().trim();
+            if (str.length() == 0) { // [JACKSON-360]
+                return null;
+            }
+            LocalDate local = parser.parseLocalDate(str);
+            if (local == null) {
+                return null;
+            }
+            return local.toDateMidnight();
+        default:
+        }
+        throw ctxt.wrongTokenException(jp, JsonToken.START_ARRAY, "expected JSON Array, Number or String");
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/DateTimeDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/DateTimeDeserializer.java
new file mode 100644
index 0000000..92b5753
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/DateTimeDeserializer.java
@@ -0,0 +1,64 @@
+package com.fasterxml.jackson.datatype.joda.deser;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+import org.joda.time.ReadableDateTime;
+import org.joda.time.ReadableInstant;
+
+import java.io.IOException;
+import java.util.TimeZone;
+
+/**
+ * Basic deserializer for {@link ReadableDateTime} and its subtypes.
+ * Accepts JSON String and Number values and passes those to single-argument constructor.
+ * Does not (yet?) support JSON object; support can be added if desired.
+ */
+public class DateTimeDeserializer
+    extends JodaDeserializerBase<ReadableInstant>
+{
+    private static final long serialVersionUID = 1L;
+
+    @SuppressWarnings("unchecked")
+    public DateTimeDeserializer(Class<? extends ReadableInstant> cls) {
+        super((Class<ReadableInstant>)cls);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T extends ReadableInstant> JsonDeserializer<T> forType(Class<T> cls)
+    {
+        return (JsonDeserializer<T>) new DateTimeDeserializer(cls);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    public ReadableDateTime deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        TimeZone tz = ctxt.getTimeZone();
+        DateTimeZone dtz = (tz == null) ? DateTimeZone.UTC : DateTimeZone.forTimeZone(tz);
+
+        if (t == JsonToken.VALUE_NUMBER_INT) {
+            return new DateTime(jp.getLongValue(), dtz);
+        }
+        if (t == JsonToken.VALUE_STRING) {
+            String str = jp.getText().trim();
+            if (str.length() == 0) { // [JACKSON-360]
+                return null;
+            }
+            if (ctxt.isEnabled(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE))
+                return new DateTime(str, dtz);
+            else
+                return DateTime.parse(str);
+        }
+        // TODO: in 2.4, use 'handledType()'
+        throw ctxt.mappingException(getValueClass());
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/DateTimeZoneDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/DateTimeZoneDeserializer.java
new file mode 100644
index 0000000..f8fbac0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/DateTimeZoneDeserializer.java
@@ -0,0 +1,37 @@
+package com.fasterxml.jackson.datatype.joda.deser;
+
+import java.io.IOException;
+
+import org.joda.time.DateTimeZone;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+
+/**
+ * Deserializes Joda {@link DateTimeZone}.
+ * Until https://jira.codehaus.org/browse/JACKSON-909 is fixed, here's my take.
+ * @author ceefour
+ */
+public class DateTimeZoneDeserializer extends JodaDeserializerBase<DateTimeZone>
+{
+    private static final long serialVersionUID = 1L;
+
+    public DateTimeZoneDeserializer() { super(DateTimeZone.class); }
+
+    @Override
+    public DateTimeZone deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.VALUE_NUMBER_INT) {
+            // for fun let's allow use of offsets...
+            return DateTimeZone.forOffsetHours(jp.getIntValue());
+        }
+        if (t == JsonToken.VALUE_STRING) {
+            return DateTimeZone.forID(jp.getText().trim());
+        }
+        throw ctxt.mappingException(DateTimeZone.class, JsonToken.VALUE_STRING);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/DurationDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/DurationDeserializer.java
new file mode 100644
index 0000000..268370b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/DurationDeserializer.java
@@ -0,0 +1,37 @@
+package com.fasterxml.jackson.datatype.joda.deser;
+
+import java.io.IOException;
+
+import org.joda.time.Duration;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
+
+/**
+ * Deserializes a Duration from either an int number of millis or using the {@link Duration#Duration(Object)}
+ * constructor on a JSON string. By default the only supported string format is that used by {@link
+ * Duration#toString()}. (That format for a 3,248 millisecond duration is "PT3.248S".)
+ */
+public final class DurationDeserializer extends StdScalarDeserializer<Duration>
+{
+    private static final long serialVersionUID = 1L;
+
+    public DurationDeserializer() { super(Duration.class); }
+
+    @Override
+    public Duration deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws
+        IOException, JsonProcessingException
+    {
+        switch (jsonParser.getCurrentToken()) {
+        case VALUE_NUMBER_INT: // assume it's millisecond count
+            return new Duration(jsonParser.getLongValue());
+        case VALUE_STRING:
+            return new Duration(jsonParser.getText());
+        default:
+        }
+        throw deserializationContext.mappingException("expected JSON Number or String");
+    }
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/InstantDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/InstantDeserializer.java
new file mode 100644
index 0000000..d1373f9
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/InstantDeserializer.java
@@ -0,0 +1,44 @@
+package com.fasterxml.jackson.datatype.joda.deser;
+
+import java.io.IOException;
+
+import org.joda.time.Instant;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+
+/**
+ * Basic deserializer for {@link org.joda.time.ReadableDateTime} and its subtypes.
+ * Accepts JSON String and Number values and passes those to single-argument constructor.
+ * Does not (yet?) support JSON object; support can be added if desired.
+ */
+public class InstantDeserializer
+    extends JodaDeserializerBase<Instant>
+{
+    private static final long serialVersionUID = 1L;
+
+    public InstantDeserializer() {
+        super(Instant.class);
+    }
+
+    @Override
+    public Instant deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.VALUE_NUMBER_INT) {
+            return new Instant(jp.getLongValue());
+        }
+        if (t == JsonToken.VALUE_STRING) {
+            String str = jp.getText().trim();
+            if (str.length() == 0) { // [JACKSON-360]
+                return null;
+            }
+            return new Instant(str);
+        }
+        // TODO: in 2.4, use 'handledType()'
+        throw ctxt.mappingException(Instant.class);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/IntervalDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/IntervalDeserializer.java
new file mode 100644
index 0000000..d902096
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/IntervalDeserializer.java
@@ -0,0 +1,46 @@
+package com.fasterxml.jackson.datatype.joda.deser;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonMappingException;
+
+import org.joda.time.Interval;
+
+import java.io.IOException;
+
+public class IntervalDeserializer extends JodaDeserializerBase<Interval>
+{
+    private static final long serialVersionUID = 5196071166239332742L;
+
+    public IntervalDeserializer() {
+        super(Interval.class);
+    }
+
+    @Override
+    public Interval deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException
+    {
+        JsonToken t = jsonParser.getCurrentToken();
+        if (t != JsonToken.VALUE_STRING) {
+            throw deserializationContext.mappingException("expected JSON String, got "+t);
+        }
+        String v = jsonParser.getText().trim();
+
+        int dashIndex = v.isEmpty() ? -1 : v.indexOf('-', 1);
+        if (dashIndex < 0) {
+            throw deserializationContext.weirdStringException(v, handledType(), "no hyphen found to separate start, end");
+        }
+        long start, end;
+        String str = v.substring(0, dashIndex);
+        try {
+            start = Long.valueOf(str);
+            str = v.substring(dashIndex + 1);
+            end = Long.valueOf(str);
+        } catch (NumberFormatException e) {
+            throw JsonMappingException.from(jsonParser,
+                    "Failed to parse number from '"+str+"' (full source String '"+v+"') to construct "+handledType().getName());
+        }
+        return new Interval(start, end);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/JodaDeserializerBase.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/JodaDeserializerBase.java
new file mode 100644
index 0000000..b8caed0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/JodaDeserializerBase.java
@@ -0,0 +1,21 @@
+package com.fasterxml.jackson.datatype.joda.deser;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
+import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
+
+ at SuppressWarnings("serial")
+abstract class JodaDeserializerBase<T> extends StdScalarDeserializer<T>
+{
+    protected JodaDeserializerBase(Class<T> cls) {
+        super(cls);
+    }
+
+    @Override
+    public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException, JsonProcessingException {
+        return typeDeserializer.deserializeTypedFromAny(jp, ctxt);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/LocalDateDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/LocalDateDeserializer.java
new file mode 100644
index 0000000..c8ed47f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/LocalDateDeserializer.java
@@ -0,0 +1,53 @@
+package com.fasterxml.jackson.datatype.joda.deser;
+
+import java.io.IOException;
+
+import org.joda.time.LocalDate;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+
+public class LocalDateDeserializer
+    extends JodaDeserializerBase<LocalDate>
+{
+    private static final long serialVersionUID = 1L;
+
+    final static DateTimeFormatter parser = ISODateTimeFormat.localDateParser();
+
+    public LocalDateDeserializer() { super(LocalDate.class); }
+
+    @Override
+    public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        // [yyyy,mm,dd]
+        if (jp.isExpectedStartArrayToken()) {
+            jp.nextToken(); // VALUE_NUMBER_INT 
+            int year = jp.getIntValue(); 
+            jp.nextToken(); // VALUE_NUMBER_INT
+            int month = jp.getIntValue();
+            jp.nextToken(); // VALUE_NUMBER_INT
+            int day = jp.getIntValue();
+            if (jp.nextToken() != JsonToken.END_ARRAY) {
+                throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY, "after LocalDate ints");
+            }
+            return new LocalDate(year, month, day);
+        }
+        switch (jp.getCurrentToken()) {
+        case VALUE_NUMBER_INT:
+            return new LocalDate(jp.getLongValue());            
+        case VALUE_STRING:
+            String str = jp.getText().trim();
+            if (str.length() == 0) { // [JACKSON-360]
+                return null;
+            }
+            return parser.parseLocalDate(str);
+        default:
+        }
+        throw ctxt.wrongTokenException(jp, JsonToken.START_ARRAY, "expected JSON Array, String or Number");
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/LocalDateTimeDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/LocalDateTimeDeserializer.java
new file mode 100644
index 0000000..7daabc8
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/LocalDateTimeDeserializer.java
@@ -0,0 +1,69 @@
+package com.fasterxml.jackson.datatype.joda.deser;
+
+import java.io.IOException;
+
+import org.joda.time.LocalDateTime;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+
+
+public class LocalDateTimeDeserializer
+    extends JodaDeserializerBase<LocalDateTime>
+{
+    private static final long serialVersionUID = 1L;
+
+    final static DateTimeFormatter parser = ISODateTimeFormat.localDateOptionalTimeParser();
+
+    public LocalDateTimeDeserializer() { super(LocalDateTime.class); }
+
+    @Override
+    public LocalDateTime deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        switch (jp.getCurrentToken()) {
+        case START_ARRAY:
+            // [yyyy,mm,dd,hh,MM,ss,ms]
+            if (jp.isExpectedStartArrayToken()) {
+                jp.nextToken(); // VALUE_NUMBER_INT
+                int year = jp.getIntValue();
+                jp.nextToken(); // VALUE_NUMBER_INT
+                int month = jp.getIntValue();
+                jp.nextToken(); // VALUE_NUMBER_INT
+                int day = jp.getIntValue();
+                jp.nextToken(); // VALUE_NUMBER_INT
+                int hour = jp.getIntValue();
+                jp.nextToken(); // VALUE_NUMBER_INT
+                int minute = jp.getIntValue();
+                jp.nextToken(); // VALUE_NUMBER_INT
+                int second = jp.getIntValue();
+                jp.nextToken(); // VALUE_NUMBER_INT | END_ARRAY
+                // let's leave milliseconds optional?
+                int millisecond = 0;
+                if (jp.getCurrentToken() != JsonToken.END_ARRAY) { // VALUE_NUMBER_INT           
+                    millisecond = jp.getIntValue();
+                    jp.nextToken(); // END_ARRAY?
+                }
+                if (jp.getCurrentToken() != JsonToken.END_ARRAY) {
+                    throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY, "after LocalDateTime ints");
+                }
+                return new LocalDateTime(year, month, day, hour, minute, second, millisecond);                 
+            }
+            break;
+        case VALUE_NUMBER_INT:
+            return new LocalDateTime(jp.getLongValue());            
+        case VALUE_STRING:
+            String str = jp.getText().trim();
+            if (str.length() == 0) { // [JACKSON-360]
+                return null;
+            }
+            return parser.parseLocalDateTime(str);
+        default:
+        }
+        throw ctxt.wrongTokenException(jp, JsonToken.START_ARRAY, "expected JSON Array, Number or String");
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/LocalTimeDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/LocalTimeDeserializer.java
new file mode 100644
index 0000000..6e92888
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/LocalTimeDeserializer.java
@@ -0,0 +1,59 @@
+package com.fasterxml.jackson.datatype.joda.deser;
+
+import java.io.IOException;
+
+import org.joda.time.LocalTime;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.databind.DeserializationContext;
+
+public class LocalTimeDeserializer
+    extends JodaDeserializerBase<LocalTime>
+{
+    private static final long serialVersionUID = 1L;
+
+    final static DateTimeFormatter parser = ISODateTimeFormat.localTimeParser();
+
+    public LocalTimeDeserializer() { super(LocalTime.class); }
+
+    @Override
+    public LocalTime deserialize(JsonParser jp, DeserializationContext ctxt)
+        throws IOException, JsonProcessingException
+    {
+        switch (jp.getCurrentToken()) {
+        case START_ARRAY:
+            // [HH,MM,ss,ms?]
+            if (jp.isExpectedStartArrayToken()) {
+                jp.nextToken(); // VALUE_NUMBER_INT 
+                int hour = jp.getIntValue(); 
+                jp.nextToken(); // VALUE_NUMBER_INT
+                int minute = jp.getIntValue();
+                jp.nextToken(); // VALUE_NUMBER_INT
+                int second = jp.getIntValue();
+                jp.nextToken(); // VALUE_NUMBER_INT | END_ARRAY
+                int millis = 0;
+                if (jp.getCurrentToken() != JsonToken.END_ARRAY) {
+                    millis = jp.getIntValue();
+                    jp.nextToken(); // END_ARRAY?
+                }
+                if (jp.getCurrentToken() != JsonToken.END_ARRAY) {
+                    throw ctxt.wrongTokenException(jp, JsonToken.END_ARRAY, "after LocalTime ints");
+                }
+                return new LocalTime(hour, minute, second, millis);
+            }
+            break;
+        case VALUE_NUMBER_INT:
+            return new LocalTime(jp.getLongValue());            
+        case VALUE_STRING:
+            String str = jp.getText().trim();
+            if (str.length() == 0) { // [JACKSON-360]
+                return null;
+            }
+            return parser.parseLocalTime(str);
+        default:
+        }
+        throw ctxt.wrongTokenException(jp, JsonToken.START_ARRAY, "expected JSON Array, String or Number");
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/MonthDayDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/MonthDayDeserializer.java
new file mode 100644
index 0000000..46fa442
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/MonthDayDeserializer.java
@@ -0,0 +1,42 @@
+package com.fasterxml.jackson.datatype.joda.deser;
+
+import java.io.IOException;
+
+import org.joda.time.MonthDay;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+
+/**
+ * A Jackson deserializer for Joda MonthDay objects.
+ * <p>
+ * Expects a string value compatible with MonthDay's parse operation.
+ */
+public class MonthDayDeserializer extends JodaDeserializerBase<MonthDay>
+{
+
+    private static final long serialVersionUID = -2360834248497553111L;
+
+    public MonthDayDeserializer()
+    {
+        super(MonthDay.class);
+    }
+
+    @Override
+    public MonthDay deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException
+    {
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.VALUE_STRING)
+        {
+            String str = jp.getText().trim();
+            if (str.isEmpty())
+            {
+                return null;
+            }
+            return MonthDay.parse(str);
+        }
+        throw ctxt.wrongTokenException(jp, JsonToken.VALUE_STRING, "expected JSON String");
+    }
+
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/PeriodDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/PeriodDeserializer.java
new file mode 100644
index 0000000..f00f32b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/PeriodDeserializer.java
@@ -0,0 +1,34 @@
+package com.fasterxml.jackson.datatype.joda.deser;
+
+import java.io.IOException;
+
+import org.joda.time.Period;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+
+public class PeriodDeserializer
+    extends JodaDeserializerBase<Period>
+{
+    private static final long serialVersionUID = 1L;
+
+    public PeriodDeserializer() { super(Period.class); }
+   
+    @Override
+    public Period deserialize(JsonParser jp, DeserializationContext ctxt)
+            throws IOException, JsonProcessingException
+    {
+        // TODO: perhaps support array of numbers...
+        //if (jp.isExpectedStartArrayToken()) { ]
+        switch (jp.getCurrentToken()) {
+        case VALUE_NUMBER_INT: // assume it's millisecond count
+            return new Period(jp.getLongValue());            
+        case VALUE_STRING:
+            return new Period(jp.getText());
+        default:
+        }
+        throw ctxt.wrongTokenException(jp, JsonToken.START_ARRAY, "expected JSON Number or String");
+   }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/ReadablePeriodDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/ReadablePeriodDeserializer.java
new file mode 100644
index 0000000..7178005
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/ReadablePeriodDeserializer.java
@@ -0,0 +1,53 @@
+package com.fasterxml.jackson.datatype.joda.deser;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+
+import org.joda.time.*;
+
+public class ReadablePeriodDeserializer extends JodaDeserializerBase<ReadablePeriod>
+{
+    private static final long serialVersionUID = 1L;
+
+    public ReadablePeriodDeserializer() {
+        super(ReadablePeriod.class);
+    }
+
+    @Override
+    public ReadablePeriod deserialize(JsonParser jsonParser, DeserializationContext ctxt)
+        throws IOException
+    {
+        JsonNode treeNode = jsonParser.readValueAsTree();
+        String periodType = treeNode.path("fieldType").path("name").asText();
+        String periodName = treeNode.path("periodType").path("name").asText();
+        // any "weird" numbers we should worry about?
+        int periodValue = treeNode.path(periodType).asInt();
+        if (periodName.equals( "Seconds" )) {
+            return Seconds.seconds( periodValue );
+        }
+        if (periodName.equals( "Minutes" )) {
+            return Minutes.minutes( periodValue );
+        }
+        if (periodName.equals( "Hours" )) {
+            return Hours.hours( periodValue );
+        }
+        if (periodName.equals( "Days" )) {
+            return Days.days( periodValue );
+        }
+        if (periodName.equals( "Weeks" )) {
+            return Weeks.weeks( periodValue );
+        }
+        if (periodName.equals( "Months" )) {
+            return Months.months( periodValue );
+        }
+        if (periodName.equals( "Years" )) {
+            return Years.years( periodValue );
+        }
+        throw ctxt.mappingException("Don't know how to deserialize ReadablePeriod using periodName '"
+                +periodName+"'");
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/YearMonthDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/YearMonthDeserializer.java
new file mode 100644
index 0000000..ffb515d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/YearMonthDeserializer.java
@@ -0,0 +1,42 @@
+package com.fasterxml.jackson.datatype.joda.deser;
+
+import java.io.IOException;
+
+import org.joda.time.YearMonth;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+
+/**
+ * A Jackson deserializer for Joda YearMonth objects.
+ * <p>
+ * Expects a string value compatible with YearMonth's parse operation.
+ */
+public class YearMonthDeserializer extends JodaDeserializerBase<YearMonth>
+{
+
+    private static final long serialVersionUID = -3830851040664795250L;
+
+    public YearMonthDeserializer()
+    {
+        super(YearMonth.class);
+    }
+
+    @Override
+    public YearMonth deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException
+    {
+        JsonToken t = jp.getCurrentToken();
+        if (t == JsonToken.VALUE_STRING)
+        {
+            String str = jp.getText().trim();
+            if (str.isEmpty())
+            {
+                return null;
+            }
+            return YearMonth.parse(str);
+        }
+        throw ctxt.wrongTokenException(jp, JsonToken.VALUE_STRING, "expected JSON String");
+    }
+
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/key/DateTimeKeyDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/key/DateTimeKeyDeserializer.java
new file mode 100644
index 0000000..dfd1a49
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/key/DateTimeKeyDeserializer.java
@@ -0,0 +1,17 @@
+package com.fasterxml.jackson.datatype.joda.deser.key;
+
+import java.io.IOException;
+
+import org.joda.time.*;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+
+public class DateTimeKeyDeserializer extends JodaKeyDeserializer {
+
+    @Override
+    protected DateTime deserialize(String key, DeserializationContext ctxt) throws IOException, JsonProcessingException{
+        return new DateTime(key, DateTimeZone.forTimeZone(ctxt.getTimeZone()));
+    }
+
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/key/JodaKeyDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/key/JodaKeyDeserializer.java
new file mode 100644
index 0000000..f08f9a9
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/key/JodaKeyDeserializer.java
@@ -0,0 +1,21 @@
+package com.fasterxml.jackson.datatype.joda.deser.key;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.KeyDeserializer;
+
+import java.io.IOException;
+
+abstract class JodaKeyDeserializer extends KeyDeserializer {
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    public final Object deserializeKey(String key, DeserializationContext ctxt) throws IOException, JsonProcessingException {
+        if (key.length() == 0) { // [JACKSON-360]
+            return null;
+        }
+        return deserialize(key, ctxt);
+    }
+
+    protected abstract Object deserialize(String key, DeserializationContext ctxt) throws IOException, JsonProcessingException;
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/key/LocalDateKeyDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/key/LocalDateKeyDeserializer.java
new file mode 100644
index 0000000..88154cf
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/key/LocalDateKeyDeserializer.java
@@ -0,0 +1,18 @@
+package com.fasterxml.jackson.datatype.joda.deser.key;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import org.joda.time.LocalDate;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+
+import java.io.IOException;
+
+public class LocalDateKeyDeserializer extends JodaKeyDeserializer {
+    private static final DateTimeFormatter parser = ISODateTimeFormat.localDateParser();
+
+    @Override
+    protected LocalDate deserialize(String key, DeserializationContext ctxt) throws IOException, JsonProcessingException {
+        return parser.parseLocalDate(key);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/key/LocalDateTimeKeyDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/key/LocalDateTimeKeyDeserializer.java
new file mode 100644
index 0000000..d766760
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/key/LocalDateTimeKeyDeserializer.java
@@ -0,0 +1,18 @@
+package com.fasterxml.jackson.datatype.joda.deser.key;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import org.joda.time.LocalDateTime;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+
+import java.io.IOException;
+
+public class LocalDateTimeKeyDeserializer extends JodaKeyDeserializer {
+    private static final DateTimeFormatter parser = ISODateTimeFormat.localDateOptionalTimeParser();
+
+    @Override
+    protected LocalDateTime deserialize(String key, DeserializationContext ctxt) throws IOException, JsonProcessingException {
+        return parser.parseLocalDateTime(key);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/deser/key/LocalTimeKeyDeserializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/key/LocalTimeKeyDeserializer.java
new file mode 100644
index 0000000..c74da5b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/deser/key/LocalTimeKeyDeserializer.java
@@ -0,0 +1,18 @@
+package com.fasterxml.jackson.datatype.joda.deser.key;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import org.joda.time.LocalTime;
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+
+import java.io.IOException;
+
+public class LocalTimeKeyDeserializer extends JodaKeyDeserializer {
+    private static final DateTimeFormatter parser = ISODateTimeFormat.localTimeParser();
+
+    @Override
+    protected LocalTime deserialize(String key, DeserializationContext ctxt) throws IOException, JsonProcessingException {
+        return parser.parseLocalTime(key);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/ser/DateMidnightSerializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/DateMidnightSerializer.java
new file mode 100644
index 0000000..d76e15b
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/DateMidnightSerializer.java
@@ -0,0 +1,48 @@
+package com.fasterxml.jackson.datatype.joda.ser;
+
+import java.io.IOException;
+
+import org.joda.time.DateMidnight;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+public final class DateMidnightSerializer
+    extends JodaDateSerializerBase<DateMidnight>
+{
+    protected final static JacksonJodaFormat DEFAULT_FORMAT
+        = new JacksonJodaFormat(DEFAULT_DATEONLY_FORMAT);
+    
+    public DateMidnightSerializer() { this(DEFAULT_FORMAT); }
+    public DateMidnightSerializer(JacksonJodaFormat format) {
+        super(DateMidnight.class, format);
+    }
+
+    @Override
+    public DateMidnightSerializer withFormat(JacksonJodaFormat formatter) {
+        return (_format == formatter) ? this : new DateMidnightSerializer(_format);
+    }
+
+    @Override
+    public void serialize(DateMidnight value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        if (_useTimestamp(provider)) {
+            // same as with other date-only values
+            jgen.writeStartArray();
+            jgen.writeNumber(value.year().get());
+            jgen.writeNumber(value.monthOfYear().get());
+            jgen.writeNumber(value.dayOfMonth().get());
+            jgen.writeEndArray();
+        } else {
+            jgen.writeString(_format.createFormatter(provider).print(value));
+        }
+    }
+
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, java.lang.reflect.Type typeHint) {
+        return createSchemaNode(_useTimestamp(provider) ? "array" : "string", true);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/ser/DateTimeSerializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/DateTimeSerializer.java
new file mode 100644
index 0000000..29f6856
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/DateTimeSerializer.java
@@ -0,0 +1,45 @@
+package com.fasterxml.jackson.datatype.joda.ser;
+
+import java.io.IOException;
+
+import org.joda.time.DateTime;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+import org.joda.time.format.ISODateTimeFormat;
+
+public final class DateTimeSerializer
+    extends JodaDateSerializerBase<DateTime>
+{
+    protected final static JacksonJodaFormat DEFAULT_FORMAT
+        = new JacksonJodaFormat(ISODateTimeFormat.dateTime().withZoneUTC());
+    
+    public DateTimeSerializer() { this(DEFAULT_FORMAT); }
+    public DateTimeSerializer(JacksonJodaFormat format) {
+        super(DateTime.class, format);
+    }
+
+    @Override
+    public DateTimeSerializer withFormat(JacksonJodaFormat formatter) {
+        return (_format == formatter) ? this : new DateTimeSerializer(formatter);
+    }
+
+    @Override
+    public void serialize(DateTime value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        if (_useTimestamp(provider)) {
+            jgen.writeNumber(value.getMillis());
+        } else {
+            jgen.writeString(_format.createFormatter(provider).print(value));
+        }
+    }
+
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, java.lang.reflect.Type typeHint) {
+        return createSchemaNode(_useTimestamp(provider) ? "number" : "string", true);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/ser/DateTimeZoneSerializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/DateTimeZoneSerializer.java
new file mode 100644
index 0000000..fab5f76
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/DateTimeZoneSerializer.java
@@ -0,0 +1,21 @@
+package com.fasterxml.jackson.datatype.joda.ser;
+
+import java.io.IOException;
+
+import org.joda.time.DateTimeZone;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+public class DateTimeZoneSerializer extends JodaSerializerBase<DateTimeZone>
+{
+    public DateTimeZoneSerializer() { super(DateTimeZone.class); }
+
+    @Override
+    public void serialize(DateTimeZone value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonProcessingException
+    {
+        jgen.writeString(value.getID());
+	}
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/ser/DurationSerializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/DurationSerializer.java
new file mode 100644
index 0000000..60a0dfe
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/DurationSerializer.java
@@ -0,0 +1,46 @@
+package com.fasterxml.jackson.datatype.joda.ser;
+
+import java.io.IOException;
+
+import org.joda.time.Duration;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+/**
+ * Serializes a Duration; either as number of millis, or, if textual output
+ * requested, using ISO-8601 format.
+ */
+public final class DurationSerializer
+    extends JodaDateSerializerBase<Duration>
+{
+    protected final static JacksonJodaFormat DEFAULT_FORMAT = new JacksonJodaFormat(DEFAULT_DATEONLY_FORMAT);
+
+    public DurationSerializer() { this(DEFAULT_FORMAT); }
+    public DurationSerializer(JacksonJodaFormat formatter) {
+        super(Duration.class, formatter);
+    }
+
+    @Override
+    public DurationSerializer withFormat(JacksonJodaFormat formatter) {
+        return (_format == formatter) ? this : new DurationSerializer(formatter);
+    }
+
+    @Override
+    public void serialize(Duration value, JsonGenerator jgen, SerializerProvider provider) throws IOException,
+        JsonProcessingException
+    {
+        if (_useTimestamp(provider)) {
+            jgen.writeNumber(value.getMillis());
+        } else {
+            jgen.writeString(value.toString());
+        }
+    }
+
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, java.lang.reflect.Type typeHint) {
+        return createSchemaNode(_useTimestamp(provider) ? "number" : "string", true);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/ser/InstantSerializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/InstantSerializer.java
new file mode 100644
index 0000000..ab10b49
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/InstantSerializer.java
@@ -0,0 +1,42 @@
+package com.fasterxml.jackson.datatype.joda.ser;
+
+import java.io.IOException;
+
+import org.joda.time.Instant;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+public final class InstantSerializer
+    extends JodaDateSerializerBase<Instant>
+{
+    protected final static JacksonJodaFormat DEFAULT_FORMAT = new JacksonJodaFormat(DEFAULT_DATEONLY_FORMAT);
+    
+    public InstantSerializer() { this(DEFAULT_FORMAT); }
+    public InstantSerializer(JacksonJodaFormat format) {
+        super(Instant.class, format);
+    }
+
+    @Override
+    public InstantSerializer withFormat(JacksonJodaFormat formatter) {
+        return (_format == formatter) ? this : new InstantSerializer(formatter);
+    }
+
+    @Override
+    public void serialize(Instant value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        if (_useTimestamp(provider)) {
+            jgen.writeNumber(value.getMillis());
+        } else {
+            jgen.writeString(value.toString());
+        }
+    }
+
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, java.lang.reflect.Type typeHint) {
+        return createSchemaNode(_useTimestamp(provider) ? "number" : "string", true);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/ser/IntervalSerializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/IntervalSerializer.java
new file mode 100644
index 0000000..f5b8552
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/IntervalSerializer.java
@@ -0,0 +1,24 @@
+package com.fasterxml.jackson.datatype.joda.ser;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+import org.joda.time.Interval;
+
+import java.io.IOException;
+
+/**
+ * @author jkolobok
+ */
+public class IntervalSerializer extends JodaSerializerBase<Interval> {
+
+    public IntervalSerializer() {
+        super(Interval.class);
+    }
+
+    @Override
+    public void serialize(Interval interval, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonGenerationException {
+        jsonGenerator.writeString(interval.getStartMillis() + "-" + interval.getEndMillis());
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/ser/JacksonJodaFormat.java b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/JacksonJodaFormat.java
new file mode 100644
index 0000000..ba46f6d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/JacksonJodaFormat.java
@@ -0,0 +1,184 @@
+package com.fasterxml.jackson.datatype.joda.ser;
+
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.joda.time.DateTimeZone;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+/**
+ * Simple container used to encapsulate (some of) gory details of
+ * customizations related to date/time formatting.
+ */
+public class JacksonJodaFormat
+{
+    private final static String JODA_STYLE_CHARS = "SMLF-";
+
+    protected final static Locale DEFAULT_LOCALE;
+    static {
+        DEFAULT_LOCALE = Locale.getDefault();
+    }
+
+    protected final DateTimeFormatter _formatter;
+    
+    /**
+     * Flag that indicates that serialization must be done as the
+     * Java timestamp, regardless of other settings.
+     */
+    protected final Boolean _useTimestamp;
+    
+    protected final TimeZone _jdkTimezone;
+
+    protected final boolean _explicitTimezone;
+    
+    protected final Locale _locale;
+
+    protected final boolean _explicitLocale;
+    
+    public JacksonJodaFormat(DateTimeFormatter defaultFormatter) {
+        _useTimestamp = null;
+        _jdkTimezone = defaultFormatter.getZone().toTimeZone();
+        _locale = DEFAULT_LOCALE;
+        _formatter = defaultFormatter;
+        _explicitTimezone = false;
+        _explicitLocale = false;
+    }
+
+    public JacksonJodaFormat(JacksonJodaFormat base, Boolean useTimestamp)
+    {
+        _useTimestamp = useTimestamp;
+        _formatter = base._formatter;
+        _jdkTimezone = base._jdkTimezone;
+        _explicitTimezone = base._explicitTimezone;
+        _locale = base._locale;
+        _explicitLocale = base._explicitLocale;
+    }
+    
+    public JacksonJodaFormat(JacksonJodaFormat base,
+            DateTimeFormatter formatter)
+    {
+        _useTimestamp = base._useTimestamp;
+        _formatter = formatter;
+        _jdkTimezone = base._jdkTimezone;
+        _explicitTimezone = base._explicitTimezone;
+        _locale = base._locale;
+        _explicitLocale = base._explicitLocale;
+    }
+
+    public JacksonJodaFormat(JacksonJodaFormat base, TimeZone jdkTimezone)
+    {
+        _useTimestamp = base._useTimestamp;
+        _jdkTimezone = jdkTimezone;
+        _explicitTimezone = true;
+        _locale = base._locale;
+        _explicitLocale = base._explicitLocale;
+        _formatter = base._formatter.withZone(DateTimeZone.forTimeZone(jdkTimezone));
+    }
+
+    public JacksonJodaFormat(JacksonJodaFormat base, Locale locale)
+    {
+        _useTimestamp = base._useTimestamp;
+        _jdkTimezone = base._jdkTimezone;
+        _explicitTimezone = base._explicitTimezone;
+        _locale = locale;
+        _explicitLocale = true;
+        _formatter = base._formatter.withLocale(locale);
+    }
+
+    /*
+    /**********************************************************
+    /* Factory methods
+    /**********************************************************
+     */
+
+    protected JacksonJodaFormat withUseTimestamp(Boolean useTimestamp) {
+        if (_useTimestamp != null && _useTimestamp.equals(useTimestamp)) {
+            return this;
+        }
+        return new JacksonJodaFormat(this, useTimestamp);
+    }
+    
+    protected JacksonJodaFormat withFormat(String format) {
+        if (format == null || format.isEmpty()) {
+            return this;
+        }
+        DateTimeFormatter formatter;
+
+        if (_isStyle(format)) {
+            formatter = DateTimeFormat.forStyle(format);
+        } else {
+            formatter = DateTimeFormat.forPattern(format);
+        }
+        if (_locale != null) {
+            formatter = formatter.withLocale(_locale);
+        }
+        formatter = formatter.withZone(_formatter.getZone());
+        return new JacksonJodaFormat(this, formatter);
+    }
+    
+    protected JacksonJodaFormat withTimeZone(TimeZone tz) {
+        if ((tz == null) || (_jdkTimezone != null && _jdkTimezone.equals(tz))) {
+            return this;
+        }
+        return new JacksonJodaFormat(this, tz);
+    }
+
+    protected JacksonJodaFormat withLocale(Locale locale) {
+        if ((locale == null) || (_locale != null && _locale.equals(locale))) {
+            return this;
+        }
+        return new JacksonJodaFormat(this, locale);
+    }
+
+    /*
+    /**********************************************************
+    /* Factory methods for other types
+    /**********************************************************
+     */
+
+    public boolean useTimestamp(SerializerProvider provider)
+    {
+        if (_useTimestamp != null) {
+            return _useTimestamp.booleanValue();
+        }
+        return provider.isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+    }
+    
+    public DateTimeFormatter createFormatter(SerializerProvider provider)
+    {
+        DateTimeFormatter formatter = _formatter;
+        
+        if (!_explicitLocale) {
+            Locale loc = provider.getLocale();
+            if (loc != null && !loc.equals(_locale)) {
+                formatter = formatter.withLocale(loc);
+            }
+        }
+        if (!_explicitTimezone) {
+            TimeZone tz = provider.getTimeZone();
+            if (tz != null && !tz.equals(_jdkTimezone)) {
+                formatter = formatter.withZone(DateTimeZone.forTimeZone(tz));
+            }
+        }
+        
+        return formatter;
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+    
+    protected static boolean _isStyle(String formatStr) {
+        if (formatStr.length() != 2) {
+            return false;
+        }
+        return (JODA_STYLE_CHARS.indexOf(formatStr.charAt(0)) >= 0)
+                && (JODA_STYLE_CHARS.indexOf(formatStr.charAt(0)) >= 0);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/ser/JodaDateSerializerBase.java b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/JodaDateSerializerBase.java
new file mode 100644
index 0000000..a642c52
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/JodaDateSerializerBase.java
@@ -0,0 +1,78 @@
+package com.fasterxml.jackson.datatype.joda.ser;
+
+import org.joda.time.format.DateTimeFormatter;
+import org.joda.time.format.ISODateTimeFormat;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.ser.ContextualSerializer;
+
+public abstract class JodaDateSerializerBase<T> extends JodaSerializerBase<T>
+// need contextualization to read per-property annotations
+    implements ContextualSerializer
+{
+    protected final static DateTimeFormatter DEFAULT_DATEONLY_FORMAT
+        = ISODateTimeFormat.date().withZoneUTC();
+
+    protected final static DateTimeFormatter DEFAULT_TIMEONLY_FORMAT
+        = ISODateTimeFormat.time().withZoneUTC();
+
+    protected final static DateTimeFormatter DEFAULT_LOCAL_DATETIME_FORMAT
+        = ISODateTimeFormat.dateTime().withZoneUTC();
+
+    protected final JacksonJodaFormat _format;
+    
+    protected JodaDateSerializerBase(Class<T> type, JacksonJodaFormat format)
+    {
+        super(type);
+        _format = format;
+    }
+
+    public abstract JodaDateSerializerBase<T> withFormat(JacksonJodaFormat format);
+
+    @Override
+    public JsonSerializer<?> createContextual(SerializerProvider prov,
+            BeanProperty property) throws JsonMappingException
+    {
+        if (property != null) {
+            JsonFormat.Value ann = prov.getAnnotationIntrospector().findFormat((Annotated)property.getMember());
+            if (ann != null) {
+                JacksonJodaFormat format = _format;
+
+                Boolean useTimestamp;
+
+                // Simple case first: serialize as numeric timestamp?
+                if (ann.getShape().isNumeric()) {
+                    useTimestamp = Boolean.TRUE;
+                } else if (ann.getShape() == JsonFormat.Shape.STRING) {
+                    useTimestamp = Boolean.FALSE;
+                } else  {
+                    useTimestamp = null;
+                }
+                // must not call if flag defined, to rely on defaults:
+                if (useTimestamp != null) {
+                    format = format.withUseTimestamp(useTimestamp);
+                }
+                // for others, safe to call, null/empty just ignored
+                format = format.withFormat(ann.getPattern().trim());
+                format = format.withLocale(ann.getLocale());
+                format = format.withTimeZone(ann.getTimeZone());
+                if (format != _format) {
+                    return withFormat(format);
+                }
+            }
+        }
+        return this;
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    protected boolean _useTimestamp(SerializerProvider provider) {
+        return _format.useTimestamp(provider);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/ser/JodaSerializerBase.java b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/JodaSerializerBase.java
new file mode 100644
index 0000000..c74f175
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/JodaSerializerBase.java
@@ -0,0 +1,21 @@
+package com.fasterxml.jackson.datatype.joda.ser;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
+import com.fasterxml.jackson.databind.ser.std.StdSerializer;
+
+abstract class JodaSerializerBase<T> extends StdSerializer<T>
+{
+    protected JodaSerializerBase(Class<T> cls) { super(cls); }
+
+    @Override
+    public void serializeWithType(T value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) throws IOException, JsonProcessingException {
+        typeSer.writeTypePrefixForScalar(value, jgen);
+        serialize(value, jgen, provider);
+        typeSer.writeTypeSuffixForScalar(value, jgen);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/ser/LocalDateSerializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/LocalDateSerializer.java
new file mode 100644
index 0000000..51bae6f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/LocalDateSerializer.java
@@ -0,0 +1,48 @@
+package com.fasterxml.jackson.datatype.joda.ser;
+
+import java.io.IOException;
+
+import org.joda.time.LocalDate;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+public final class LocalDateSerializer
+    extends JodaDateSerializerBase<LocalDate>
+{
+    protected final static JacksonJodaFormat DEFAULT_FORMAT
+        = new JacksonJodaFormat(DEFAULT_DATEONLY_FORMAT);
+
+    public LocalDateSerializer() { this(DEFAULT_FORMAT); }
+    public LocalDateSerializer(JacksonJodaFormat format) {
+        super(LocalDate.class, format);
+    }
+
+    @Override
+    public LocalDateSerializer withFormat(JacksonJodaFormat formatter) {
+        return (_format == formatter) ? this : new LocalDateSerializer(formatter);
+    }
+    
+    @Override
+    public void serialize(LocalDate value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        if (_useTimestamp(provider)) {
+            // Timestamp here actually means an array of values
+            jgen.writeStartArray();
+            jgen.writeNumber(value.year().get());
+            jgen.writeNumber(value.monthOfYear().get());
+            jgen.writeNumber(value.dayOfMonth().get());
+            jgen.writeEndArray();
+        } else {
+            jgen.writeString(_format.createFormatter(provider).print(value));
+        }
+    }
+
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, java.lang.reflect.Type typeHint) {
+        return createSchemaNode(_useTimestamp(provider) ? "array" : "string", true);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/ser/LocalDateTimeSerializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/LocalDateTimeSerializer.java
new file mode 100644
index 0000000..a361cdd
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/LocalDateTimeSerializer.java
@@ -0,0 +1,52 @@
+package com.fasterxml.jackson.datatype.joda.ser;
+
+import java.io.IOException;
+
+import org.joda.time.LocalDateTime;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+public final class LocalDateTimeSerializer
+    extends JodaDateSerializerBase<LocalDateTime>
+{
+    protected final static JacksonJodaFormat DEFAULT_FORMAT
+        = new JacksonJodaFormat(DEFAULT_LOCAL_DATETIME_FORMAT);
+
+    public LocalDateTimeSerializer() { this(DEFAULT_FORMAT); }
+    public LocalDateTimeSerializer(JacksonJodaFormat format) {
+        super(LocalDateTime.class, format);
+    }
+
+    @Override
+    public LocalDateTimeSerializer withFormat(JacksonJodaFormat formatter) {
+        return (_format == formatter) ? this : new LocalDateTimeSerializer(formatter);
+    }
+
+    @Override
+    public void serialize(LocalDateTime value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        if (_useTimestamp(provider)) {
+            // Timestamp here actually means an array of values
+            jgen.writeStartArray();
+            jgen.writeNumber(value.year().get());
+            jgen.writeNumber(value.monthOfYear().get());
+            jgen.writeNumber(value.dayOfMonth().get());
+            jgen.writeNumber(value.hourOfDay().get());
+            jgen.writeNumber(value.minuteOfHour().get());
+            jgen.writeNumber(value.secondOfMinute().get());
+            jgen.writeNumber(value.millisOfSecond().get());
+            jgen.writeEndArray();
+        } else {
+            jgen.writeString(_format.createFormatter(provider).print(value));
+        }
+    }
+
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, java.lang.reflect.Type typeHint) {
+        return createSchemaNode(_useTimestamp(provider) ? "array" : "string", true);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/ser/LocalTimeSerializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/LocalTimeSerializer.java
new file mode 100644
index 0000000..841895c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/LocalTimeSerializer.java
@@ -0,0 +1,49 @@
+package com.fasterxml.jackson.datatype.joda.ser;
+
+import java.io.IOException;
+
+import org.joda.time.LocalTime;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+public final class LocalTimeSerializer
+    extends JodaDateSerializerBase<LocalTime>
+{
+    protected final static JacksonJodaFormat DEFAULT_FORMAT
+        = new JacksonJodaFormat(DEFAULT_TIMEONLY_FORMAT);
+
+    public LocalTimeSerializer() { this(DEFAULT_FORMAT); }
+    public LocalTimeSerializer(JacksonJodaFormat format) {
+        super(LocalTime.class, format);
+    }
+
+    @Override
+    public LocalTimeSerializer withFormat(JacksonJodaFormat formatter) {
+        return (_format == formatter) ? this : new LocalTimeSerializer(formatter);
+    }
+
+    @Override
+    public void serialize(LocalTime value, JsonGenerator jgen, SerializerProvider provider)
+        throws IOException, JsonGenerationException
+    {
+        if (_useTimestamp(provider)) {
+            // Timestamp here actually means an array of values
+            jgen.writeStartArray();
+            jgen.writeNumber(value.hourOfDay().get());
+            jgen.writeNumber(value.minuteOfHour().get());
+            jgen.writeNumber(value.secondOfMinute().get());
+            jgen.writeNumber(value.millisOfSecond().get());
+            jgen.writeEndArray();
+        } else {
+            jgen.writeString(_format.createFormatter(provider).print(value));
+        }
+    }
+
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, java.lang.reflect.Type typeHint) {
+        return createSchemaNode(_useTimestamp(provider) ? "array" : "string", true);
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/fasterxml/jackson/datatype/joda/ser/PeriodSerializer.java b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/PeriodSerializer.java
new file mode 100644
index 0000000..ee3cff3
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/datatype/joda/ser/PeriodSerializer.java
@@ -0,0 +1,37 @@
+package com.fasterxml.jackson.datatype.joda.ser;
+
+import java.io.IOException;
+
+import org.joda.time.ReadablePeriod;
+import org.joda.time.format.ISOPeriodFormat;
+import org.joda.time.format.PeriodFormatter;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+/**
+ * Serializes a {@link ReadablePeriod} using Joda default formatting.
+ *<p>
+ * TODO: allow serialization as an array of numbers, for numeric ("timestamp")
+ * notation?
+ */
+public final class PeriodSerializer extends JodaSerializerBase<ReadablePeriod>
+{
+    protected final PeriodFormatter defaultFormat = ISOPeriodFormat.standard();
+
+    public PeriodSerializer() { super(ReadablePeriod.class); }
+
+    @Override
+    public void serialize(ReadablePeriod value, JsonGenerator jgen, SerializerProvider provider) throws IOException,
+        JsonProcessingException
+    {
+        jgen.writeString(defaultFormat.print(value));
+    }
+
+    @Override
+    public JsonNode getSchema(SerializerProvider provider, java.lang.reflect.Type typeHint) {
+        return createSchemaNode("string", true);
+    }
+}
diff --git a/src/main/resources/META-INF/LICENSE b/src/main/resources/META-INF/LICENSE
new file mode 100644
index 0000000..f5f45d2
--- /dev/null
+++ b/src/main/resources/META-INF/LICENSE
@@ -0,0 +1,8 @@
+This copy of Jackson JSON processor streaming parser/generator is licensed under the
+Apache (Software) License, version 2.0 ("the License").
+See the License for details about distribution rights, and the
+specific rights regarding derivate works.
+
+You may obtain a copy of the License at:
+
+http://www.apache.org/licenses/LICENSE-2.0
diff --git a/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module b/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module
new file mode 100644
index 0000000..f21f03f
--- /dev/null
+++ b/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module
@@ -0,0 +1 @@
+com.fasterxml.jackson.datatype.joda.JodaModule
diff --git a/src/test/java/com/fasterxml/jackson/datatype/joda/DateTimeTest.java b/src/test/java/com/fasterxml/jackson/datatype/joda/DateTimeTest.java
new file mode 100644
index 0000000..f7da384
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/joda/DateTimeTest.java
@@ -0,0 +1,100 @@
+package com.fasterxml.jackson.datatype.joda;
+
+import java.io.IOException;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+
+public class DateTimeTest extends JodaTestBase
+{
+    private static class DateAsText {
+        @JsonFormat(shape=JsonFormat.Shape.STRING)
+        public DateTime date;
+
+        public DateAsText(DateTime d) {
+            date = d;
+        }
+    }
+
+    private static class CustomDate {
+        @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="SS")
+        public DateTime date;
+
+        public CustomDate(DateTime d) {
+            date = d;
+        }
+    }
+    
+    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY, property = "@class")
+    private static interface ObjectConfiguration {
+    }
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+    
+    private final ObjectMapper MAPPER = jodaMapper();
+
+    private final static ObjectMapper STRING_MAPPER = jodaMapper();
+    static {
+        STRING_MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+    }
+
+    private final DateTime DATE_JAN_1_1970_UTC = new DateTime(0L, DateTimeZone.UTC);
+    
+    /**
+     * First: let's ensure that serialization does not fail
+     * with an error (see [JACKSON-157]).
+     */
+    public void testSerializationDefaultAsTimestamp() throws IOException
+    {
+        // let's use epoch time (Jan 1, 1970, UTC)
+        // by default, dates use timestamp, so:
+        assertEquals("0", MAPPER.writeValueAsString(DATE_JAN_1_1970_UTC));
+    }
+
+    public void testSerializationFeatureNoTimestamp() throws IOException
+    {
+        ObjectMapper m = jodaMapper();
+        m.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+        assertEquals(quote("1970-01-01T00:00:00.000Z"), m.writeValueAsString(DATE_JAN_1_1970_UTC));
+    }
+
+    public void testAnnotationAsText() throws IOException
+    {
+        ObjectMapper m = jodaMapper();
+        m.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+        // with annotations, doesn't matter if mapper configured to use timestamps
+        assertEquals(aposToQuotes("{'date':'1970-01-01T00:00:00.000Z'}"),
+                m.writeValueAsString(new DateAsText(DATE_JAN_1_1970_UTC)));
+    }
+
+    public void testCustomPatternStyle() throws IOException
+    {
+        // or, using annotations
+        assertEquals(aposToQuotes("{'date':'1/1/70 12:00 AM'}"),
+                STRING_MAPPER.writeValueAsString(new CustomDate(DATE_JAN_1_1970_UTC)));
+    }
+    
+    public void testSerializationWithTypeInfo() throws IOException
+    {
+        // let's use epoch time (Jan 1, 1970, UTC)
+        DateTime dt = new DateTime(0L, DateTimeZone.UTC);
+        // by default, dates use timestamp, so:
+        assertEquals("0", MAPPER.writeValueAsString(dt));
+
+        // but if re-configured, as regular ISO-8601 string
+        ObjectMapper m = jodaMapper();
+        m.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+        m.addMixInAnnotations(DateTime.class, ObjectConfiguration.class);
+        assertEquals("[\"org.joda.time.DateTime\",\"1970-01-01T00:00:00.000Z\"]",
+                m.writeValueAsString(dt));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/datatype/joda/JodaMapperTest.java b/src/test/java/com/fasterxml/jackson/datatype/joda/JodaMapperTest.java
new file mode 100644
index 0000000..fbf1e7a
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/joda/JodaMapperTest.java
@@ -0,0 +1,32 @@
+package com.fasterxml.jackson.datatype.joda;
+
+import com.fasterxml.jackson.databind.SerializationFeature;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+public class JodaMapperTest
+{
+    @Test
+    public void test_writeDatesAsTimestamps_property()
+    {
+        JodaMapper objectUnderTest = new JodaMapper();
+
+        objectUnderTest.setWriteDatesAsTimestamps(true);
+        assertThat(objectUnderTest.getWriteDatesAsTimestamps(), is(true));
+
+        objectUnderTest.setWriteDatesAsTimestamps(false);
+        assertThat(objectUnderTest.getWriteDatesAsTimestamps(), is(false));
+    }
+
+    @Test
+    public void setWriteDatesAsTimestamps_sets_the_WRITE_DATES_AS_TIMESTAMPS_configuration()
+    {
+        JodaMapper objectUnderTest = new JodaMapper();
+
+        objectUnderTest.setWriteDatesAsTimestamps(true);
+
+        assertThat(objectUnderTest.isEnabled(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS), is(true));
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/datatype/joda/JodaSerializationTest.java b/src/test/java/com/fasterxml/jackson/datatype/joda/JodaSerializationTest.java
new file mode 100644
index 0000000..4037391
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/joda/JodaSerializationTest.java
@@ -0,0 +1,246 @@
+package com.fasterxml.jackson.datatype.joda;
+
+import java.io.IOException;
+
+import org.joda.time.*;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+
+public class JodaSerializationTest extends JodaTestBase
+{
+    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY, property = "@class")
+    private static interface ObjectConfiguration {
+    }
+
+    private final ObjectMapper MAPPER = jodaMapper();
+   
+    /*
+    /**********************************************************
+    /* Tests for DateMidnight type
+    /**********************************************************
+     */
+    
+    public void testDateMidnightSer() throws IOException
+    {
+        DateMidnight date = new DateMidnight(2001, 5, 25);
+        // default format is that of JSON array...
+        assertEquals("[2001,5,25]", MAPPER.writeValueAsString(date));
+        // but we can force it to be a String as well (note: here we assume this is
+        // dynamically changeable)
+        ObjectMapper mapper = jodaMapper();
+        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);        
+        assertEquals(quote("2001-05-25"), mapper.writeValueAsString(date));
+    }
+    
+    public void testDateMidnightSerWithTypeInfo() throws IOException
+    {
+        DateMidnight date = new DateMidnight(2001, 5, 25);
+        // default format is that of JSON array...
+        assertEquals("[2001,5,25]", MAPPER.writeValueAsString(date));
+        // but we can force it to be a String as well (note: here we assume this is
+        // dynamically changeable)
+        ObjectMapper mapper = jodaMapper();
+        mapper.addMixInAnnotations(DateMidnight.class, ObjectConfiguration.class);
+        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);        
+        assertEquals("[\"org.joda.time.DateMidnight\",\"2001-05-25\"]", mapper.writeValueAsString(date));
+    }
+
+    /*
+    /**********************************************************
+    /* Tests for Interval type
+    /**********************************************************
+     */
+
+    public void testIntervalSer() throws IOException
+    {
+        Interval interval = new Interval(1396439982, 1396440001);
+        assertEquals(quote("1396439982-1396440001"), MAPPER.writeValueAsString(interval));
+    }
+
+    public void testIntervalSerWithTypeInfo() throws IOException
+    {
+        Interval interval = new Interval(1396439982, 1396440001);
+
+        ObjectMapper mapper = jodaMapper();
+        mapper.addMixInAnnotations(Interval.class, ObjectConfiguration.class);
+        assertEquals("[\"org.joda.time.Interval\"," + quote("1396439982-1396440001") + "]",
+                mapper.writeValueAsString(interval));
+    }
+    
+    /*
+    /**********************************************************
+    /* Tests for LocalDate type
+    /**********************************************************
+     */
+    
+    public void testLocalDateSer() throws IOException
+    {
+        LocalDate date = new LocalDate(2001, 5, 25);
+        // default format is that of JSON array...
+        assertEquals("[2001,5,25]", MAPPER.writeValueAsString(date));
+
+        // but we can force it to be a String as well (note: here we assume this is
+        // dynamically changeable)
+        ObjectMapper mapper = jodaMapper();
+        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);        
+        assertEquals(quote("2001-05-25"), mapper.writeValueAsString(date));
+    }
+    
+    public void testLocalDateSerWithTypeInfo() throws IOException
+    {
+        LocalDate date = new LocalDate(2001, 5, 25);
+        // default format is that of JSON array...
+        assertEquals("[2001,5,25]", MAPPER.writeValueAsString(date));
+
+        // but we can force it to be a String as well (note: here we assume this is
+        // dynamically changeable)
+        ObjectMapper mapper = jodaMapper();
+        mapper.addMixInAnnotations(LocalDate.class, ObjectConfiguration.class);
+        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);        
+        assertEquals("[\"org.joda.time.LocalDate\",\"2001-05-25\"]", mapper.writeValueAsString(date));
+    }
+
+    
+    /*
+    /**********************************************************
+    /* Tests for LocalTime type
+    /**********************************************************
+     */
+    
+    public void testLocalTimeSer() throws IOException
+    {
+        LocalTime date = new LocalTime(13,20,54);
+        // default format is that of JSON array...
+        assertEquals("[13,20,54,0]", MAPPER.writeValueAsString(date));
+
+        // but we can force it to be a String as well (note: here we assume this is
+        // dynamically changeable)
+        ObjectMapper mapper = jodaMapper();
+        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);        
+        assertEquals(quote("13:20:54.000"), mapper.writeValueAsString(date));
+    }
+    
+    public void testLocalTimeSerWithTypeInfo() throws IOException
+    {
+        LocalTime date = new LocalTime(13,20,54);
+        // default format is that of JSON array...
+        assertEquals("[13,20,54,0]", MAPPER.writeValueAsString(date));
+
+        // but we can force it to be a String as well (note: here we assume this is
+        // dynamically changeable)
+        ObjectMapper mapper = jodaMapper();
+        mapper.addMixInAnnotations(LocalTime.class, ObjectConfiguration.class);
+        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);        
+        assertEquals("[\"org.joda.time.LocalTime\",\"13:20:54.000\"]", mapper.writeValueAsString(date));
+    }
+
+    /*
+    /**********************************************************
+    /* Tests for LocalDateTime type
+    /**********************************************************
+     */
+    
+    public void testLocalDateTimeSer() throws IOException
+    {
+        LocalDateTime date = new LocalDateTime(2001, 5, 25,
+                10, 15, 30, 37);
+        // default format is that of JSON array...
+        assertEquals("[2001,5,25,10,15,30,37]", MAPPER.writeValueAsString(date));
+        // but we can force it to be a String as well (note: here we assume this is
+        // dynamically changeable)
+        ObjectMapper mapper = jodaMapper();
+        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);        
+        assertEquals(quote("2001-05-25T10:15:30.037"), mapper.writeValueAsString(date));
+    }
+    
+    public void testLocalDateTimeSerWithTypeInfo() throws IOException
+    {
+        LocalDateTime date = new LocalDateTime(2001, 5, 25,
+                10, 15, 30, 37);
+        // default format is that of JSON array...
+        assertEquals("[2001,5,25,10,15,30,37]", MAPPER.writeValueAsString(date));
+        // but we can force it to be a String as well (note: here we assume this is
+        // dynamically changeable)
+        ObjectMapper mapper = jodaMapper();
+        mapper.addMixInAnnotations(LocalDateTime.class, ObjectConfiguration.class);
+        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);        
+        assertEquals("[\"org.joda.time.LocalDateTime\",\"2001-05-25T10:15:30.037\"]", mapper.writeValueAsString(date));
+    }
+
+    /*
+    /**********************************************************
+    /* Tests for Period type
+    /**********************************************************
+     */
+
+    public void testPeriodSer() throws IOException
+    {
+        Period in = new Period(1, 2, 3, 4);
+        assertEquals(quote("PT1H2M3.004S"), MAPPER.writeValueAsString(in));
+    }
+    
+    public void testPeriodSerWithTypeInfo() throws IOException
+    {
+        Period in = new Period(1, 2, 3, 4);
+        ObjectMapper mapper = jodaMapper();
+        mapper.addMixInAnnotations(Period.class, ObjectConfiguration.class);
+        assertEquals("[\"org.joda.time.Period\",\"PT1H2M3.004S\"]", mapper.writeValueAsString(in));
+    }
+
+    /*
+    /**********************************************************
+    /* Tests for Duration type
+    /**********************************************************
+     */
+
+    public void testDurationSer() throws IOException
+    {
+        Duration d = new Duration(3123422);
+        String json = MAPPER.writeValueAsString(d);
+        assertEquals("3123422", json);
+
+        assertEquals(quote("PT3123.422S"), MAPPER.writer()
+                .without(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
+                .writeValueAsString(d));
+    }
+    
+    public void testDurationSerWithTypeInfo() throws IOException
+    {
+        Duration d = new Duration(3123422);
+        ObjectMapper mapper = jodaMapper();
+        mapper.addMixInAnnotations(Duration.class, ObjectConfiguration.class);
+        String json = mapper.writeValueAsString(d);
+        assertEquals("[\"org.joda.time.Duration\",3123422]", json);
+    }
+
+    public void testInstantSer() throws IOException {
+        Instant instant = new Instant(0L);
+
+        // by default, dates use timestamp, so:
+        assertEquals("0", MAPPER.writeValueAsString(instant));
+
+        // but if re-configured, as regular ISO-8601 string
+        ObjectMapper m = jodaMapper();
+        m.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
+        assertEquals(quote("1970-01-01T00:00:00.000Z"), m.writeValueAsString(instant));
+    }
+
+    public void testMonthDaySer() throws Exception
+    {
+        MonthDay monthDay = new MonthDay(7, 23);
+        ObjectMapper mapper = jodaMapper();
+        String json = mapper.writeValueAsString(monthDay);
+        assertEquals(quote("--07-23"), json);
+    }
+
+    public void testYearMonthSer() throws Exception
+    {
+        YearMonth yearMonth = new YearMonth(2013, 8);
+        ObjectMapper mapper = jodaMapper();
+        String json = mapper.writeValueAsString(yearMonth);
+        assertEquals(quote("2013-08"), json);
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/datatype/joda/JodaTestBase.java b/src/test/java/com/fasterxml/jackson/datatype/joda/JodaTestBase.java
new file mode 100644
index 0000000..d22d1bd
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/joda/JodaTestBase.java
@@ -0,0 +1,46 @@
+package com.fasterxml.jackson.datatype.joda;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import junit.framework.TestCase;
+
+import java.io.IOException;
+
+import static org.junit.Assert.*;
+
+public abstract class JodaTestBase extends TestCase
+{
+    protected static ObjectMapper jodaMapper()
+    {
+        return new JodaMapper();
+    }
+
+    /*
+    /**********************************************************
+    /* Additional assert methods
+    /**********************************************************
+     */
+
+    protected void assertEquals(int[] exp, int[] act) {
+        assertArrayEquals(exp, act);
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    public String quote(String str) {
+        return '"'+str+'"';
+    }
+
+    protected String aposToQuotes(String json) {
+        return json.replace("'", "\"");
+    }
+
+    protected <T> T readAndMapFromString(ObjectMapper m, String input, Class<T> cls)
+        throws IOException
+    {
+        return (T) m.readValue("\""+input+"\"", cls);
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/datatype/joda/MixedListTest.java b/src/test/java/com/fasterxml/jackson/datatype/joda/MixedListTest.java
new file mode 100644
index 0000000..c529c96
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/joda/MixedListTest.java
@@ -0,0 +1,42 @@
+package com.fasterxml.jackson.datatype.joda;
+
+import java.util.*;
+
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeZone;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class MixedListTest extends JodaTestBase
+{
+    private final ObjectMapper MAPPER = jodaMapper();
+
+    public void testMixedList() throws Exception
+    {
+        final Map<String, Object> map = new HashMap<String, Object>();
+        DateTime dt = new DateTime(DateTimeZone.UTC);
+        map.put("A", dt);
+        map.put("B", 0);
+    	
+	    final String json = MAPPER.writeValueAsString(map);
+	    // by default, timestamps should come out as longs...
+	    
+	    final Map<String, Object> result = MAPPER.readValue(json,
+	    		new TypeReference<Map<String, Object>>() { });
+
+	    assertEquals(2, result.size());
+	    Object obB = result.get("B");
+	    assertNotNull(obB);
+	    if (!(obB instanceof Number)) {
+	        fail("Expected 'B' to be a Number; instead of value of type "+obB.getClass().getName());
+	    }
+	    
+	    assertEquals(Integer.valueOf(0), result.get("B"));
+	    Object obA = result.get("A");
+	    assertNotNull(obA);
+	    if (!(obA instanceof Number)) {
+	        fail("Expected 'A' to be a number; instead of value of type "+obA.getClass().getName());
+	    }
+    }    
+}
diff --git a/src/test/java/com/fasterxml/jackson/datatype/joda/TestVersions.java b/src/test/java/com/fasterxml/jackson/datatype/joda/TestVersions.java
new file mode 100644
index 0000000..d82af95
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/joda/TestVersions.java
@@ -0,0 +1,29 @@
+package com.fasterxml.jackson.datatype.joda;
+
+import java.io.*;
+
+import com.fasterxml.jackson.core.Versioned;
+
+/**
+ * Simple verification that version access works.
+ */
+public class TestVersions extends JodaTestBase
+{
+    public void testVersions() throws IOException
+    {
+        JodaModule m = new JodaModule();
+        assertVersion(m);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    private void assertVersion(Versioned v)
+    {
+        assertEquals(PackageVersion.VERSION, v.version());
+    }
+}
+
diff --git a/src/test/java/com/fasterxml/jackson/datatype/joda/deser/MiscDeserializationTest.java b/src/test/java/com/fasterxml/jackson/datatype/joda/deser/MiscDeserializationTest.java
new file mode 100644
index 0000000..20209e1
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/joda/deser/MiscDeserializationTest.java
@@ -0,0 +1,516 @@
+package com.fasterxml.jackson.datatype.joda.deser;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.joda.JodaTestBase;
+
+import org.joda.time.*;
+
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.Map;
+import java.util.TimeZone;
+
+/**
+ * Unit tests for verifying limited interoperability for Joda time.
+ * Basic support is added for handling {@link DateTime}; more can be
+ * added over time if and when requested.
+ */
+public class MiscDeserializationTest extends JodaTestBase
+{
+    /*
+    /**********************************************************
+    /* Tests for DateTime (and closely related)
+    /**********************************************************
+     */
+
+    private final ObjectMapper MAPPER = jodaMapper();
+
+    /**
+     * Ok, then: should be able to convert from JSON String or Number,
+     * with standard deserializer we provide.
+     */
+    public void testDeserFromNumber() throws IOException
+    {
+        Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
+        // use some arbitrary but non-default time point (after 1.1.1970)
+        cal.set(Calendar.YEAR, 1972);
+        long timepoint = cal.getTime().getTime();
+
+        // Ok, first: using JSON number (milliseconds since epoch)
+        DateTime dt = MAPPER.readValue(String.valueOf(timepoint), DateTime.class);
+        assertEquals(timepoint, dt.getMillis());
+
+        // And then ISO-8601 String
+        dt = MAPPER.readValue(quote("1972-12-28T12:00:01.000+0000"), DateTime.class);
+        assertEquals("1972-12-28T12:00:01.000Z", dt.toString());
+    }
+
+    public void testDeserReadableDateTime() throws IOException
+    {
+        ReadableDateTime date = MAPPER.readValue(quote("1972-12-28T12:00:01.000+0000"), ReadableDateTime.class);
+        assertNotNull(date);
+        assertEquals("1972-12-28T12:00:01.000Z", date.toString());
+
+        // since 1.6.1, for [JACKSON-360]
+        assertNull(MAPPER.readValue(quote(""), ReadableDateTime.class));
+    }
+
+    // since 2.1.3, for github issue #8
+    public void testDeserReadableDateTimeWithTimeZoneInfo() throws IOException {
+        TimeZone timeZone = TimeZone.getTimeZone("GMT-6");
+        DateTimeZone dateTimeZone = DateTimeZone.forTimeZone(timeZone);
+        MAPPER.setTimeZone(timeZone);
+        ReadableDateTime date = MAPPER.readValue(quote("1972-12-28T12:00:01.000-0600"), ReadableDateTime.class);
+        assertNotNull(date);
+        assertEquals("1972-12-28T12:00:01.000-06:00", date.toString());
+        assertEquals(dateTimeZone, date.getZone());
+
+        // default behavior is to ignore the timezone in serialized data
+        ReadableDateTime otherTzDate = MAPPER.readValue(quote("1972-12-28T12:00:01.000-0700"), ReadableDateTime.class);
+        assertEquals(dateTimeZone, otherTzDate.getZone());
+
+        // since 1.6.1, for [JACKSON-360]
+        assertNull(MAPPER.readValue(quote(""), ReadableDateTime.class));
+    }
+
+    public void testDeserReadableDateTimeWithTimeZoneFromData() throws IOException {
+        ObjectMapper mapper = jodaMapper();
+        mapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
+        mapper.setTimeZone(TimeZone.getTimeZone("GMT-6"));
+        ReadableDateTime date = mapper.readValue(quote("2014-01-20T08:59:01.000-0500"), ReadableDateTime.class);
+        assertEquals(DateTimeZone.forOffsetHours(-5), date.getZone());
+    }
+
+    public void testDeserReadableInstant() throws IOException {
+        ReadableInstant date = MAPPER.readValue(quote("1972-12-28T12:00:01.000+0000"), ReadableInstant.class);
+        assertNotNull(date);
+        assertEquals("1972-12-28T12:00:01.000Z", date.toString());
+
+        // since 1.6.1, for [JACKSON-360]
+        assertNull(MAPPER.readValue(quote(""), ReadableInstant.class));
+    }
+
+    public void testDeserDateTimeWithTypeInfo() throws IOException
+    {
+        ObjectMapper mapper = jodaMapper();
+        mapper.addMixInAnnotations(DateTime.class, ObjectConfiguration.class);
+        DateTime date = mapper.readValue("[\"org.joda.time.DateTime\",\"1972-12-28T12:00:01.000+0000\"]", DateTime.class);
+        assertNotNull(date);
+        assertEquals("1972-12-28T12:00:01.000Z", date.toString());
+    }
+
+    /*
+    /**********************************************************
+    /* Tests for DateMidnight type
+    /**********************************************************
+     */
+
+    public void testDateMidnightDeser() throws IOException
+    {
+        // couple of acceptable formats, so:
+        DateMidnight date = MAPPER.readValue("[2001,5,25]", DateMidnight.class);
+        assertEquals(2001, date.getYear());
+        assertEquals(5, date.getMonthOfYear());
+        assertEquals(25, date.getDayOfMonth());
+
+        DateMidnight date2 = MAPPER.readValue(quote("2005-07-13"), DateMidnight.class);
+        assertEquals(2005, date2.getYear());
+        assertEquals(7, date2.getMonthOfYear());
+        assertEquals(13, date2.getDayOfMonth());
+
+        // since 1.6.1, for [JACKSON-360]
+        assertNull(MAPPER.readValue(quote(""), DateMidnight.class));
+    }
+
+    public void testDateMidnightDeserWithTypeInfo() throws IOException
+    {
+        ObjectMapper mapper = jodaMapper();
+        mapper.addMixInAnnotations(DateMidnight.class, ObjectConfiguration.class);
+
+        // couple of acceptable formats, so:
+        DateMidnight date = mapper.readValue("[\"org.joda.time.DateMidnight\",[2001,5,25]]", DateMidnight.class);
+        assertEquals(2001, date.getYear());
+        assertEquals(5, date.getMonthOfYear());
+        assertEquals(25, date.getDayOfMonth());
+
+        DateMidnight date2 = mapper.readValue("[\"org.joda.time.DateMidnight\",\"2005-07-13\"]", DateMidnight.class);
+        assertEquals(2005, date2.getYear());
+        assertEquals(7, date2.getMonthOfYear());
+        assertEquals(13, date2.getDayOfMonth());
+    }
+    
+    /*
+    /**********************************************************
+    /* Tests for LocalDate type
+    /**********************************************************
+     */
+
+    public void testLocalDateDeser() throws IOException
+    {
+        // couple of acceptable formats, so:
+        LocalDate date = MAPPER.readValue("[2001,5,25]", LocalDate.class);
+        assertEquals(2001, date.getYear());
+        assertEquals(5, date.getMonthOfYear());
+        assertEquals(25, date.getDayOfMonth());
+
+        LocalDate date2 = MAPPER.readValue(quote("2005-07-13"), LocalDate.class);
+        assertEquals(2005, date2.getYear());
+        assertEquals(7, date2.getMonthOfYear());
+        assertEquals(13, date2.getDayOfMonth());
+
+        // since 1.6.1, for [JACKSON-360]
+        assertNull(MAPPER.readValue(quote(""), LocalDate.class));
+    }
+
+    public void testLocalDateDeserWithTypeInfo() throws IOException
+    {
+        ObjectMapper mapper = jodaMapper();
+        mapper.addMixInAnnotations(LocalDate.class, ObjectConfiguration.class);
+
+        // couple of acceptable formats, so:
+        LocalDate date = mapper.readValue("[\"org.joda.time.LocalDate\",[2001,5,25]]", LocalDate.class);
+        assertEquals(2001, date.getYear());
+        assertEquals(5, date.getMonthOfYear());
+        assertEquals(25, date.getDayOfMonth());
+
+        LocalDate date2 = mapper.readValue("[\"org.joda.time.LocalDate\",\"2005-07-13\"]", LocalDate.class);
+        assertEquals(2005, date2.getYear());
+        assertEquals(7, date2.getMonthOfYear());
+        assertEquals(13, date2.getDayOfMonth());
+    }
+    
+    /*
+    /**********************************************************
+    /* Tests for LocalTime type
+    /**********************************************************
+     */
+
+    public void testLocalTimeDeser() throws IOException
+    {
+        // couple of acceptable formats, so:
+        LocalTime time = MAPPER.readValue("[23,59,1,222]", LocalTime.class);
+        assertEquals(23, time.getHourOfDay());
+        assertEquals(59, time.getMinuteOfHour());
+        assertEquals(1, time.getSecondOfMinute());
+        assertEquals(222, time.getMillisOfSecond());
+
+        LocalTime time2 = MAPPER.readValue(quote("13:45:22"), LocalTime.class);
+        assertEquals(13, time2.getHourOfDay());
+        assertEquals(45, time2.getMinuteOfHour());
+        assertEquals(22, time2.getSecondOfMinute());
+        assertEquals(0, time2.getMillisOfSecond());
+
+        // since 1.6.1, for [JACKSON-360]
+        assertNull(MAPPER.readValue(quote(""), LocalTime.class));
+    }
+
+    public void testLocalTimeDeserWithTypeInfo() throws IOException
+    {
+        ObjectMapper mapper = jodaMapper();
+        mapper.addMixInAnnotations(LocalTime.class, ObjectConfiguration.class);
+
+        // couple of acceptable formats, so:
+        LocalTime time = mapper.readValue("[\"org.joda.time.LocalTime\",[23,59,1,10]]", LocalTime.class);
+        assertEquals(23, time.getHourOfDay());
+        assertEquals(59, time.getMinuteOfHour());
+        assertEquals(1, time.getSecondOfMinute());
+        assertEquals(10, time.getMillisOfSecond());
+
+        LocalTime time2 = mapper.readValue("[\"org.joda.time.LocalTime\",\"13:45:22\"]", LocalTime.class);
+        assertEquals(13, time2.getHourOfDay());
+        assertEquals(45, time2.getMinuteOfHour());
+        assertEquals(22, time2.getSecondOfMinute());
+        assertEquals(0, time2.getMillisOfSecond());
+    }
+
+    /*
+    /**********************************************************
+    /* Tests for Interval type
+    /**********************************************************
+     */
+    public void testIntervalDeser() throws IOException
+    {
+        Interval interval = MAPPER.readValue(quote("1396439982-1396440001"), Interval.class);
+        assertEquals(1396439982, interval.getStartMillis());
+        assertEquals(1396440001, interval.getEndMillis());
+
+        interval = MAPPER.readValue(quote("-100-1396440001"), Interval.class);
+        assertEquals(-100, interval.getStartMillis());
+        assertEquals(1396440001, interval.getEndMillis());
+    }
+
+    public void testIntervalDeserWithTypeInfo() throws IOException
+    {
+        ObjectMapper mapper = jodaMapper();
+        mapper.addMixInAnnotations(Interval.class, ObjectConfiguration.class);
+
+        Interval interval= mapper.readValue("[\"org.joda.time.Interval\",\"1396439982-1396440001\"]", Interval.class);
+        assertEquals(1396439982, interval.getStartMillis());
+        assertEquals(1396440001, interval.getEndMillis());
+    }
+
+    /*
+    /**********************************************************
+    /* Tests for LocalDateTime type
+    /**********************************************************
+     */
+
+    public void testLocalDateTimeDeser() throws IOException
+    {
+        // couple of acceptable formats again:
+        LocalDateTime date = MAPPER.readValue("[2001,5,25,10,15,30,37]", LocalDateTime.class);
+        assertEquals(2001, date.getYear());
+        assertEquals(5, date.getMonthOfYear());
+        assertEquals(25, date.getDayOfMonth());
+
+        assertEquals(10, date.getHourOfDay());
+        assertEquals(15, date.getMinuteOfHour());
+        assertEquals(30, date.getSecondOfMinute());
+        assertEquals(37, date.getMillisOfSecond());
+
+        LocalDateTime date2 = MAPPER.readValue(quote("2007-06-30T08:34:09.001"), LocalDateTime.class);
+        assertEquals(2007, date2.getYear());
+        assertEquals(6, date2.getMonthOfYear());
+        assertEquals(30, date2.getDayOfMonth());
+
+        assertEquals(8, date2.getHourOfDay());
+        assertEquals(34, date2.getMinuteOfHour());
+        assertEquals(9, date2.getSecondOfMinute());
+        assertEquals(1, date2.getMillisOfSecond());
+
+        // since 1.6.1, for [JACKSON-360]
+        assertNull(MAPPER.readValue(quote(""), LocalDateTime.class));
+    }
+
+    public void testLocalDateTimeDeserWithTypeInfo() throws IOException
+    {
+        ObjectMapper mapper = jodaMapper();
+        mapper.addMixInAnnotations(LocalDateTime.class, ObjectConfiguration.class);
+
+        // couple of acceptable formats again:
+        LocalDateTime date = mapper.readValue("[\"org.joda.time.LocalDateTime\",[2001,5,25,10,15,30,37]]", LocalDateTime.class);
+        assertEquals(2001, date.getYear());
+        assertEquals(5, date.getMonthOfYear());
+        assertEquals(25, date.getDayOfMonth());
+
+        assertEquals(10, date.getHourOfDay());
+        assertEquals(15, date.getMinuteOfHour());
+        assertEquals(30, date.getSecondOfMinute());
+        assertEquals(37, date.getMillisOfSecond());
+
+        LocalDateTime date2 = mapper.readValue("[\"org.joda.time.LocalDateTime\",\"2007-06-30T08:34:09.001\"]", LocalDateTime.class);
+        assertEquals(2007, date2.getYear());
+        assertEquals(6, date2.getMonthOfYear());
+        assertEquals(30, date2.getDayOfMonth());
+
+        assertEquals(8, date2.getHourOfDay());
+        assertEquals(34, date2.getMinuteOfHour());
+        assertEquals(9, date2.getSecondOfMinute());
+        assertEquals(1, date2.getMillisOfSecond());
+    }
+
+    /*
+    /**********************************************************
+    /* Tests for Period type
+    /**********************************************************
+     */
+
+    public void testPeriodDeser() throws IOException
+    {
+        Period out = MAPPER.readValue(quote("PT1H2M3.004S"), Period.class);
+        assertEquals(1, out.getHours());
+        assertEquals(2, out.getMinutes());
+        assertEquals(3, out.getSeconds());
+        assertEquals(4, out.getMillis());
+
+        // also, should work as number:
+        String json = String.valueOf(1000 * out.toStandardSeconds().getSeconds());
+        out = MAPPER.readValue(json, Period.class);
+        assertEquals(1, out.getHours());
+        assertEquals(2, out.getMinutes());
+        assertEquals(3, out.getSeconds());
+        // but millis are actually truncated...
+        assertEquals(0, out.getMillis());
+    }
+
+    public void testPeriodDeserWithTypeInfo() throws IOException
+    {
+        ObjectMapper mapper = jodaMapper();
+        mapper.addMixInAnnotations(Period.class, ObjectConfiguration.class);
+
+        Period out = mapper.readValue("[\"org.joda.time.Period\",\"PT1H2M3.004S\"]", Period.class);
+        assertEquals(1, out.getHours());
+        assertEquals(2, out.getMinutes());
+        assertEquals(3, out.getSeconds());
+        assertEquals(4, out.getMillis());
+
+        // also, should work as number:
+        String json = "[\"org.joda.time.Period\"," + String.valueOf(1000 * out.toStandardSeconds().getSeconds()) + "]";
+        out = mapper.readValue(json, Period.class);
+        assertEquals(1, out.getHours());
+        assertEquals(2, out.getMinutes());
+        assertEquals(3, out.getSeconds());
+        // but millis are actually truncated...
+        assertEquals(0, out.getMillis());
+    }
+
+    /*
+    /**********************************************************
+    /* Tests for Duration type
+    /**********************************************************
+     */
+
+    public void testDurationDeserFromInt() throws IOException
+    {
+        Duration d = MAPPER.readValue("1234", Duration.class);
+        assertEquals(1234, d.getMillis());
+    }
+
+    public void testDurationDeserFromString() throws IOException
+    {
+        Duration d = MAPPER.readValue(quote("PT1.234S"), Duration.class);
+        assertEquals(1234, d.getMillis());
+    }
+
+    public void testDurationRoundtrip() throws IOException
+    {
+        Duration d = new Duration(5513);
+        assertEquals(d, MAPPER.readValue(MAPPER.writeValueAsString(d), Duration.class));
+    }
+
+    public void testDurationFailsDeserializingUnexpectedType() throws IOException
+    {
+        try {
+            MAPPER.readValue("{\"foo\":1234}", Duration.class);
+            fail();
+        } catch (JsonMappingException e) {
+            // there's location info involving a string object id on the second line, so just use the first line
+            assertEquals("expected JSON Number or String", e.getMessage().split("\n")[0]);
+        }
+    }
+
+    public void testDurationDeserFromIntWithTypeInfo() throws IOException
+    {
+        ObjectMapper mapper = jodaMapper();
+        mapper.addMixInAnnotations(Duration.class, ObjectConfiguration.class);
+
+        Duration d = mapper.readValue("[\"org.joda.time.Duration\",1234]", Duration.class);
+        assertEquals(1234, d.getMillis());
+    }
+
+    @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_ARRAY, property = "@class")
+    private static interface ObjectConfiguration {
+    }
+
+
+
+    public void testDeserInstantFromNumber() throws IOException
+    {
+        Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+        cal.set(Calendar.YEAR, 1972);
+        long timepoint = cal.getTime().getTime();
+
+        // Ok, first: using JSON number (milliseconds since epoch)
+        Instant instant = MAPPER.readValue(String.valueOf(timepoint), Instant.class);
+        assertEquals(timepoint, instant.getMillis());
+    }
+
+    public void testDeserInstant() throws IOException
+    {
+        Instant date = MAPPER.readValue(quote("1972-12-28T12:00:01.000Z"), Instant.class);
+        assertNotNull(date);
+        assertEquals("1972-12-28T12:00:01.000Z", date.toString());
+
+        // since 1.6.1, for [JACKSON-360]
+        assertNull(MAPPER.readValue(quote(""), Instant.class));
+    }
+
+    public void testDateTimeKeyDeserialize() throws IOException {
+
+        final String json = "{" + quote("1970-01-01T00:00:00.000Z") + ":0}";
+        final Map<DateTime, Long> map = MAPPER.readValue(json, new TypeReference<Map<DateTime, String>>() { });
+
+        assertNotNull(map);
+        assertTrue(map.containsKey(DateTime.parse("1970-01-01T00:00:00.000Z")));
+    }
+
+    public void testLocalDateKeyDeserialize() throws IOException {
+
+        final String json = "{" + quote("2014-05-23") + ":0}";
+        final Map<LocalDate, Long> map = MAPPER.readValue(json, new TypeReference<Map<LocalDate, String>>() { });
+
+        assertNotNull(map);
+        assertTrue(map.containsKey(LocalDate.parse("2014-05-23")));
+    }
+
+    public void testLocalTimeKeyDeserialize() throws IOException {
+
+        final String json = "{" + quote("00:00:00.000") + ":0}";
+        final Map<LocalTime, Long> map = MAPPER.readValue(json, new TypeReference<Map<LocalTime, String>>() { });
+        assertNotNull(map);
+        assertTrue(map.containsKey(LocalTime.parse("00:00:00.000")));
+    }
+    public void testLocalDateTimeKeyDeserialize() throws IOException {
+
+        final String json = "{" + quote("2014-05-23T00:00:00.000") + ":0}";
+        final Map<LocalDateTime, Long> map = MAPPER.readValue(json, new TypeReference<Map<LocalDateTime, String>>() { });
+        assertNotNull(map);
+        assertTrue(map.containsKey(LocalDateTime.parse("2014-05-23T00:00:00.000")));
+    }
+
+    public void testDeserMonthDay() throws Exception
+    {
+        String monthDayString = new MonthDay(7, 23).toString();
+        MonthDay monthDay = MAPPER.readValue(quote(monthDayString), MonthDay.class);
+        assertEquals(new MonthDay(7, 23), monthDay);
+    }
+
+    public void testDeserMonthDayFromEmptyString() throws Exception
+    {
+        MonthDay monthDay = MAPPER.readValue(quote(""), MonthDay.class);
+        assertNull(monthDay);
+    }
+
+    public void testDeserMonthDayFailsForUnexpectedType() throws IOException
+    {
+        try
+        {
+            MAPPER.readValue("{\"month\":8}", MonthDay.class);
+            fail();
+        } catch (JsonMappingException e)
+        {
+            assertTrue(e.getMessage().contains("expected JSON String"));
+        }
+    }
+
+    public void testDeserYearMonth() throws Exception
+    {
+        String yearMonthString = new YearMonth(2013, 8).toString();
+        YearMonth yearMonth = MAPPER.readValue(quote(yearMonthString), YearMonth.class);
+        assertEquals(new YearMonth(2013, 8), yearMonth);
+    }
+
+    public void testDeserYearMonthFromEmptyString() throws Exception
+    {
+        YearMonth yearMonth = MAPPER.readValue(quote(""), YearMonth.class);
+        assertNull(yearMonth);
+    }
+
+    public void testDeserYearMonthFailsForUnexpectedType() throws IOException
+    {
+        try
+        {
+            MAPPER.readValue("{\"year\":2013}", YearMonth.class);
+            fail();
+        } catch (JsonMappingException e)
+        {
+            assertTrue(e.getMessage().contains("expected JSON String"));
+        }
+    }
+
+}
diff --git a/src/test/java/com/fasterxml/jackson/datatype/joda/deser/ReadablePeriodDeserializerTest.java b/src/test/java/com/fasterxml/jackson/datatype/joda/deser/ReadablePeriodDeserializerTest.java
new file mode 100644
index 0000000..f3e4daf
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/datatype/joda/deser/ReadablePeriodDeserializerTest.java
@@ -0,0 +1,73 @@
+package com.fasterxml.jackson.datatype.joda.deser;
+
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.joda.JodaTestBase;
+import org.joda.time.Days;
+import org.joda.time.Hours;
+import org.joda.time.Minutes;
+import org.joda.time.Months;
+import org.joda.time.ReadablePeriod;
+import org.joda.time.Seconds;
+import org.joda.time.Weeks;
+import org.joda.time.Years;
+
+public class ReadablePeriodDeserializerTest extends JodaTestBase
+{
+
+	public void testDeserializeSeconds() throws Exception
+	{
+		ObjectMapper objectMapper = jodaMapper();
+		ReadablePeriod readablePeriod = objectMapper.readValue( "{\"fieldType\":{\"name\":\"seconds\"},\"seconds\":12,\"periodType\":{\"name\":\"Seconds\"}}", ReadablePeriod.class );
+		assertNotNull( readablePeriod );
+		assertEquals( Seconds.seconds( 12 ), readablePeriod );
+	}
+
+	public void testDeserializeMinutes() throws Exception
+	{
+		ObjectMapper objectMapper = jodaMapper();
+		ReadablePeriod readablePeriod = objectMapper.readValue( "{\"fieldType\":{\"name\":\"minutes\"},\"minutes\":1,\"periodType\":{\"name\":\"Minutes\"}}", ReadablePeriod.class );
+		assertNotNull( readablePeriod );
+		assertEquals( Minutes.minutes( 1 ), readablePeriod );
+	}
+
+	public void testDeserializeHours() throws Exception
+	{
+		ObjectMapper objectMapper = jodaMapper();
+		ReadablePeriod readablePeriod = objectMapper.readValue( "{\"fieldType\":{\"name\":\"hours\"},\"hours\":2,\"periodType\":{\"name\":\"Hours\"}}", ReadablePeriod.class );
+		assertNotNull( readablePeriod );
+		assertEquals( Hours.hours( 2 ), readablePeriod );
+	}
+
+	public void testDeserializeDays() throws Exception
+	{
+		ObjectMapper objectMapper = jodaMapper();
+		ReadablePeriod readablePeriod = objectMapper.readValue( "{\"fieldType\":{\"name\":\"days\"},\"days\":2,\"periodType\":{\"name\":\"Days\"}}", ReadablePeriod.class );
+		assertNotNull( readablePeriod );
+		assertEquals( Days.days( 2 ), readablePeriod );
+	}
+	
+	public void testDeserializeWeeks() throws Exception
+	{
+		ObjectMapper objectMapper = jodaMapper();
+		ReadablePeriod readablePeriod = objectMapper.readValue( "{\"fieldType\":{\"name\":\"weeks\"},\"weeks\":2,\"periodType\":{\"name\":\"Weeks\"}}", ReadablePeriod.class );
+		assertNotNull( readablePeriod );
+		assertEquals( Weeks.weeks( 2 ), readablePeriod );
+	}
+	
+	public void testDeserializeMonths() throws Exception
+	{
+		ObjectMapper objectMapper = jodaMapper();
+		ReadablePeriod readablePeriod = objectMapper.readValue( "{\"fieldType\":{\"name\":\"months\"},\"months\":2,\"periodType\":{\"name\":\"Months\"}}", ReadablePeriod.class );
+		assertNotNull( readablePeriod );
+		assertEquals( Months.months( 2 ), readablePeriod );
+	}
+
+	public void testDeserializeYears() throws Exception
+	{
+		ObjectMapper objectMapper = jodaMapper();
+		ReadablePeriod readablePeriod = objectMapper.readValue( "{\"fieldType\":{\"name\":\"years\"},\"years\":2,\"periodType\":{\"name\":\"Years\"}}", ReadablePeriod.class );
+		assertNotNull( readablePeriod );
+		assertEquals( Years.years( 2 ), readablePeriod );
+	}
+}
\ No newline at end of file

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/jackson-datatype-joda.git



More information about the pkg-java-commits mailing list