Add TimestampType to replace DateType patch by slebresne; reviewed by jbellis for CASSANDRA-5723
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/9ec7b808 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/9ec7b808 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/9ec7b808 Branch: refs/heads/trunk Commit: 9ec7b808ac6786db74b5cbad0a63f6a9cdfdc5bd Parents: e9c6742 Author: Sylvain Lebresne <sylv...@datastax.com> Authored: Mon Jul 8 15:49:21 2013 +0200 Committer: Sylvain Lebresne <sylv...@datastax.com> Committed: Mon Jul 8 18:55:32 2013 +0200 ---------------------------------------------------------------------- CHANGES.txt | 1 + NEWS.txt | 1 + .../org/apache/cassandra/cql3/CQL3Type.java | 2 +- .../apache/cassandra/cql3/UntypedResultSet.java | 2 +- .../cassandra/cql3/functions/TimeuuidFcts.java | 14 +- .../cassandra/db/marshal/AbstractType.java | 1 - .../apache/cassandra/db/marshal/DateType.java | 42 ++++-- .../apache/cassandra/db/marshal/LongType.java | 8 +- .../cassandra/db/marshal/TimestampType.java | 145 +++++++++++++++++++ .../apache/cassandra/db/marshal/UUIDType.java | 2 +- .../hadoop/pig/AbstractCassandraStorage.java | 4 +- .../org/apache/cassandra/tools/Shuffle.java | 4 +- .../apache/cassandra/transport/DataType.java | 5 +- .../apache/cassandra/type/DateSerializer.java | 104 ------------- .../cassandra/type/TimestampSerializer.java | 104 +++++++++++++ 15 files changed, 304 insertions(+), 135 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/9ec7b808/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index eaa5eea..4b7dcc5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -72,6 +72,7 @@ * Move resultset type information into prepare, not execute (CASSANDRA-5649) * Auto paging in binary protocol (CASSANDRA-4415, 5714) * Don't tie client side use of AbstractType to JDBC (CASSANDRA-4495) + * Adds new TimestampType to replace DateType (CASSANDRA-5723) 1.2.7 http://git-wip-us.apache.org/repos/asf/cassandra/blob/9ec7b808/NEWS.txt ---------------------------------------------------------------------- diff --git a/NEWS.txt b/NEWS.txt index 7e12e2c..652f8c2 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -73,6 +73,7 @@ Features 1.2.7 ===== + Upgrading --------- - If you have decommissioned a node in the past 72 hours, it is imperative http://git-wip-us.apache.org/repos/asf/cassandra/blob/9ec7b808/src/java/org/apache/cassandra/cql3/CQL3Type.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/CQL3Type.java b/src/java/org/apache/cassandra/cql3/CQL3Type.java index d7a4e8e..aed3165 100644 --- a/src/java/org/apache/cassandra/cql3/CQL3Type.java +++ b/src/java/org/apache/cassandra/cql3/CQL3Type.java @@ -41,7 +41,7 @@ public interface CQL3Type INET (InetAddressType.instance), INT (Int32Type.instance), TEXT (UTF8Type.instance), - TIMESTAMP(DateType.instance), + TIMESTAMP(TimestampType.instance), UUID (UUIDType.instance), VARCHAR (UTF8Type.instance), VARINT (IntegerType.instance), http://git-wip-us.apache.org/repos/asf/cassandra/blob/9ec7b808/src/java/org/apache/cassandra/cql3/UntypedResultSet.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/UntypedResultSet.java b/src/java/org/apache/cassandra/cql3/UntypedResultSet.java index 9bee563..25294d6 100644 --- a/src/java/org/apache/cassandra/cql3/UntypedResultSet.java +++ b/src/java/org/apache/cassandra/cql3/UntypedResultSet.java @@ -128,7 +128,7 @@ public class UntypedResultSet implements Iterable<UntypedResultSet.Row> public Date getTimestamp(String column) { - return DateType.instance.compose(data.get(column)); + return TimestampType.instance.compose(data.get(column)); } public long getLong(String column) http://git-wip-us.apache.org/repos/asf/cassandra/blob/9ec7b808/src/java/org/apache/cassandra/cql3/functions/TimeuuidFcts.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/functions/TimeuuidFcts.java b/src/java/org/apache/cassandra/cql3/functions/TimeuuidFcts.java index e325e8f..18ccd56 100644 --- a/src/java/org/apache/cassandra/cql3/functions/TimeuuidFcts.java +++ b/src/java/org/apache/cassandra/cql3/functions/TimeuuidFcts.java @@ -21,7 +21,7 @@ import java.nio.ByteBuffer; import java.util.Date; import java.util.List; -import org.apache.cassandra.db.marshal.DateType; +import org.apache.cassandra.db.marshal.TimestampType; import org.apache.cassandra.db.marshal.TimeUUIDType; import org.apache.cassandra.db.marshal.LongType; import org.apache.cassandra.utils.ByteBufferUtil; @@ -43,27 +43,27 @@ public abstract class TimeuuidFcts } }; - public static final Function minTimeuuidFct = new AbstractFunction("mintimeuuid", TimeUUIDType.instance, DateType.instance) + public static final Function minTimeuuidFct = new AbstractFunction("mintimeuuid", TimeUUIDType.instance, TimestampType.instance) { public ByteBuffer execute(List<ByteBuffer> parameters) { - return ByteBuffer.wrap(UUIDGen.decompose(UUIDGen.minTimeUUID(DateType.instance.compose(parameters.get(0)).getTime()))); + return ByteBuffer.wrap(UUIDGen.decompose(UUIDGen.minTimeUUID(TimestampType.instance.compose(parameters.get(0)).getTime()))); } }; - public static final Function maxTimeuuidFct = new AbstractFunction("maxtimeuuid", TimeUUIDType.instance, DateType.instance) + public static final Function maxTimeuuidFct = new AbstractFunction("maxtimeuuid", TimeUUIDType.instance, TimestampType.instance) { public ByteBuffer execute(List<ByteBuffer> parameters) { - return ByteBuffer.wrap(UUIDGen.decompose(UUIDGen.maxTimeUUID(DateType.instance.compose(parameters.get(0)).getTime()))); + return ByteBuffer.wrap(UUIDGen.decompose(UUIDGen.maxTimeUUID(TimestampType.instance.compose(parameters.get(0)).getTime()))); } }; - public static final Function dateOfFct = new AbstractFunction("dateof", DateType.instance, TimeUUIDType.instance) + public static final Function dateOfFct = new AbstractFunction("dateof", TimestampType.instance, TimeUUIDType.instance) { public ByteBuffer execute(List<ByteBuffer> parameters) { - return DateType.instance.decompose(new Date(UUIDGen.unixTimestamp(UUIDGen.getUUID(parameters.get(0))))); + return TimestampType.instance.decompose(new Date(UUIDGen.unixTimestamp(UUIDGen.getUUID(parameters.get(0))))); } }; http://git-wip-us.apache.org/repos/asf/cassandra/blob/9ec7b808/src/java/org/apache/cassandra/db/marshal/AbstractType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/AbstractType.java b/src/java/org/apache/cassandra/db/marshal/AbstractType.java index 07bf694..c9e94b7 100644 --- a/src/java/org/apache/cassandra/db/marshal/AbstractType.java +++ b/src/java/org/apache/cassandra/db/marshal/AbstractType.java @@ -306,4 +306,3 @@ public abstract class AbstractType<T> implements Comparator<ByteBuffer> return false; } } - http://git-wip-us.apache.org/repos/asf/cassandra/blob/9ec7b808/src/java/org/apache/cassandra/db/marshal/DateType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/DateType.java b/src/java/org/apache/cassandra/db/marshal/DateType.java index dcd8c9f..7de69e8 100644 --- a/src/java/org/apache/cassandra/db/marshal/DateType.java +++ b/src/java/org/apache/cassandra/db/marshal/DateType.java @@ -17,37 +17,40 @@ */ package org.apache.cassandra.db.marshal; -import static org.apache.cassandra.type.DateSerializer.iso8601Patterns; - import java.nio.ByteBuffer; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import org.apache.cassandra.cql3.CQL3Type; import org.apache.cassandra.type.AbstractSerializer; -import org.apache.cassandra.type.DateSerializer; +import org.apache.cassandra.type.TimestampSerializer; import org.apache.cassandra.type.MarshalException; import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.commons.lang.time.DateUtils; public class DateType extends AbstractType<Date> { + private static final Logger logger = LoggerFactory.getLogger(DateType.class); + public static final DateType instance = new DateType(); - static final String DEFAULT_FORMAT = iso8601Patterns[3]; + static final String DEFAULT_FORMAT = TimestampSerializer.iso8601Patterns[3]; static final SimpleDateFormat FORMATTER = new SimpleDateFormat(DEFAULT_FORMAT); DateType() {} // singleton public Date compose(ByteBuffer bytes) { - return DateSerializer.instance.serialize(bytes); + return TimestampSerializer.instance.serialize(bytes); } public ByteBuffer decompose(Date value) { - return DateSerializer.instance.deserialize(value); + return TimestampSerializer.instance.deserialize(value); } public int compare(ByteBuffer o1, ByteBuffer o2) @@ -66,7 +69,7 @@ public class DateType extends AbstractType<Date> public String getString(ByteBuffer bytes) { - return DateSerializer.instance.getString(bytes); + return TimestampSerializer.instance.getString(bytes); } public ByteBuffer fromString(String source) throws MarshalException @@ -103,7 +106,7 @@ public class DateType extends AbstractType<Date> { try { - millis = DateUtils.parseDateStrictly(source, iso8601Patterns).getTime(); + millis = DateUtils.parseDateStrictly(source, TimestampSerializer.iso8601Patterns).getTime(); } catch (ParseException e1) { @@ -114,18 +117,31 @@ public class DateType extends AbstractType<Date> return millis; } - public void validate(ByteBuffer bytes) throws MarshalException + @Override + public boolean isCompatibleWith(AbstractType<?> previous) { - DateSerializer.instance.validate(bytes); + if (super.isCompatibleWith(previous)) + return true; + + if (previous instanceof TimestampType) + { + logger.warn("Changing from TimestampType to DateType is allowed, but be wary that they sort differently for pre-unix-epoch timestamps " + + "(negative timestamp values) and thus this change will corrupt your data if you have such negative timestamp. There is no " + + "reason to switch from DateType to TimestampType except if you were using DateType in the first place and switched to " + + "TimestampType by mistake."); + return true; + } + + return false; } - public CQL3Type asCQL3Type() + public void validate(ByteBuffer bytes) throws MarshalException { - return CQL3Type.Native.TIMESTAMP; + TimestampSerializer.instance.validate(bytes); } public AbstractSerializer<Date> asComposer() { - return DateSerializer.instance; + return TimestampSerializer.instance; } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/9ec7b808/src/java/org/apache/cassandra/db/marshal/LongType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/LongType.java b/src/java/org/apache/cassandra/db/marshal/LongType.java index 45edc2f..2570418 100644 --- a/src/java/org/apache/cassandra/db/marshal/LongType.java +++ b/src/java/org/apache/cassandra/db/marshal/LongType.java @@ -43,6 +43,11 @@ public class LongType extends AbstractType<Long> public int compare(ByteBuffer o1, ByteBuffer o2) { + return compareLongs(o1, o2); + } + + public static int compareLongs(ByteBuffer o1, ByteBuffer o2) + { if (o1.remaining() == 0) { return o2.remaining() == 0 ? 0 : -1; @@ -52,11 +57,10 @@ public class LongType extends AbstractType<Long> return 1; } - int diff = o1.get(o1.position()) - o2.get(o2.position()); + int diff = o1.get(o1.position() + o1.arrayOffset()) - o2.get(o2.position() + o2.arrayOffset()); if (diff != 0) return diff; - return ByteBufferUtil.compareUnsigned(o1, o2); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/9ec7b808/src/java/org/apache/cassandra/db/marshal/TimestampType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/TimestampType.java b/src/java/org/apache/cassandra/db/marshal/TimestampType.java new file mode 100644 index 0000000..ee2140c --- /dev/null +++ b/src/java/org/apache/cassandra/db/marshal/TimestampType.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.cassandra.db.marshal; + +import java.nio.ByteBuffer; +import java.text.ParseException; +import java.util.Date; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.type.AbstractSerializer; +import org.apache.cassandra.type.MarshalException; +import org.apache.cassandra.type.TimestampSerializer; +import org.apache.cassandra.utils.ByteBufferUtil; +import org.apache.commons.lang.time.DateUtils; + +/** + * Type for date-time values. + * + * This is meant as a replacement for DateType, as DateType wrongly compare + * pre-unix-epoch dates, sorting them *after* post-unix-epoch ones (due to it's + * use of unsigned bytes comparison). + */ +public class TimestampType extends AbstractType<Date> +{ + private static final Logger logger = LoggerFactory.getLogger(TimestampType.class); + + public static final TimestampType instance = new TimestampType(); + + private TimestampType() {} // singleton + + public Date compose(ByteBuffer bytes) + { + return TimestampSerializer.instance.serialize(bytes); + } + + public ByteBuffer decompose(Date value) + { + return TimestampSerializer.instance.deserialize(value); + } + + public int compare(ByteBuffer o1, ByteBuffer o2) + { + return LongType.compareLongs(o1, o2); + } + + public String getString(ByteBuffer bytes) + { + return TimestampSerializer.instance.getString(bytes); + } + + public ByteBuffer fromString(String source) throws MarshalException + { + // Return an empty ByteBuffer for an empty string. + if (source.isEmpty()) + return ByteBufferUtil.EMPTY_BYTE_BUFFER; + + return ByteBufferUtil.bytes(dateStringToTimestamp(source)); + } + + public static long dateStringToTimestamp(String source) throws MarshalException + { + long millis; + + if (source.toLowerCase().equals("now")) + { + millis = System.currentTimeMillis(); + } + // Milliseconds since epoch? + else if (source.matches("^\\d+$")) + { + try + { + millis = Long.parseLong(source); + } + catch (NumberFormatException e) + { + throw new MarshalException(String.format("unable to make long (for date) from: '%s'", source), e); + } + } + // Last chance, attempt to parse as date-time string + else + { + try + { + millis = DateUtils.parseDateStrictly(source, TimestampSerializer.iso8601Patterns).getTime(); + } + catch (ParseException e1) + { + throw new MarshalException(String.format("unable to coerce '%s' to a formatted date (long)", source), e1); + } + } + + return millis; + } + + public void validate(ByteBuffer bytes) throws MarshalException + { + TimestampSerializer.instance.validate(bytes); + } + + @Override + public boolean isCompatibleWith(AbstractType<?> previous) + { + if (super.isCompatibleWith(previous)) + return true; + + if (previous instanceof DateType) + { + logger.warn("Changing from DateType to TimestampType is allowed, but be wary that they sort differently for pre-unix-epoch timestamps " + + "(negative timestamp values) and thus this change will corrupt your data if you have such negative timestamp. So unless you " + + "know that you don't have *any* pre-unix-epoch timestamp you should change back to DateType"); + return true; + } + + return false; + } + + public CQL3Type asCQL3Type() + { + return CQL3Type.Native.TIMESTAMP; + } + + public AbstractSerializer<Date> asComposer() + { + return TimestampSerializer.instance; + } +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/9ec7b808/src/java/org/apache/cassandra/db/marshal/UUIDType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/db/marshal/UUIDType.java b/src/java/org/apache/cassandra/db/marshal/UUIDType.java index 159cab3..73021ee 100644 --- a/src/java/org/apache/cassandra/db/marshal/UUIDType.java +++ b/src/java/org/apache/cassandra/db/marshal/UUIDType.java @@ -30,7 +30,7 @@ import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.UUIDGen; import org.apache.commons.lang.time.DateUtils; -import static org.apache.cassandra.type.DateSerializer.iso8601Patterns; +import static org.apache.cassandra.type.TimestampSerializer.iso8601Patterns; /** * Compares UUIDs using the following criteria:<br> http://git-wip-us.apache.org/repos/asf/cassandra/blob/9ec7b808/src/java/org/apache/cassandra/hadoop/pig/AbstractCassandraStorage.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/hadoop/pig/AbstractCassandraStorage.java b/src/java/org/apache/cassandra/hadoop/pig/AbstractCassandraStorage.java index d1bc5e4..9636cea 100644 --- a/src/java/org/apache/cassandra/hadoop/pig/AbstractCassandraStorage.java +++ b/src/java/org/apache/cassandra/hadoop/pig/AbstractCassandraStorage.java @@ -140,7 +140,7 @@ public abstract class AbstractCassandraStorage extends LoadFunc implements Store else if (value instanceof UUID) pair.set(position, new DataByteArray(UUIDGen.decompose((java.util.UUID) value))); else if (value instanceof Date) - pair.set(position, DateType.instance.decompose((Date) value).getLong()); + pair.set(position, TimestampType.instance.decompose((Date) value).getLong()); else pair.set(position, value); } @@ -299,7 +299,7 @@ public abstract class AbstractCassandraStorage extends LoadFunc implements Store /** get pig type for the cassandra data type*/ protected byte getPigType(AbstractType type) { - if (type instanceof LongType || type instanceof DateType) // DateType is bad and it should feel bad + if (type instanceof LongType || type instanceof DateType || type instanceof TimestampType) // DateType is bad and it should feel bad return DataType.LONG; else if (type instanceof IntegerType || type instanceof Int32Type) // IntegerType will overflow at 2**31, but is kept for compatibility until pig has a BigInteger return DataType.INTEGER; http://git-wip-us.apache.org/repos/asf/cassandra/blob/9ec7b808/src/java/org/apache/cassandra/tools/Shuffle.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/tools/Shuffle.java b/src/java/org/apache/cassandra/tools/Shuffle.java index 5749a81..0c5c92a 100644 --- a/src/java/org/apache/cassandra/tools/Shuffle.java +++ b/src/java/org/apache/cassandra/tools/Shuffle.java @@ -43,7 +43,7 @@ import javax.management.MBeanServerConnection; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; -import org.apache.cassandra.type.DateSerializer; +import org.apache.cassandra.type.TimestampSerializer; import org.apache.cassandra.dht.IPartitioner; import org.apache.cassandra.dht.Token; import org.apache.cassandra.locator.EndpointSnitchInfoMBean; @@ -389,7 +389,7 @@ public class Shuffle extends AbstractJmxClient ByteBuffer tokenBytes = ByteBuffer.wrap(row.getColumns().get(0).getValue()); ByteBuffer requestedAt = ByteBuffer.wrap(row.getColumns().get(1).getValue()); - Date time = DateSerializer.instance.serialize(requestedAt); + Date time = TimestampSerializer.instance.serialize(requestedAt); Token<?> token = partitioner.getTokenFactory().fromByteArray(tokenBytes); writeln("%-42s %-15s %s", token.toString(), host, time.toString()); http://git-wip-us.apache.org/repos/asf/cassandra/blob/9ec7b808/src/java/org/apache/cassandra/transport/DataType.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/transport/DataType.java b/src/java/org/apache/cassandra/transport/DataType.java index 21f4d03..b590042 100644 --- a/src/java/org/apache/cassandra/transport/DataType.java +++ b/src/java/org/apache/cassandra/transport/DataType.java @@ -43,7 +43,7 @@ public enum DataType implements OptionCodec.Codecable<DataType> FLOAT (8, FloatType.instance), INT (9, Int32Type.instance), TEXT (10, UTF8Type.instance), - TIMESTAMP(11, DateType.instance), + TIMESTAMP(11, TimestampType.instance), UUID (12, UUIDType.instance), VARCHAR (13, UTF8Type.instance), VARINT (14, IntegerType.instance), @@ -146,6 +146,9 @@ public enum DataType implements OptionCodec.Codecable<DataType> // shouldn't have to care about it. if (type instanceof ReversedType) type = ((ReversedType)type).baseType; + // For compatibility sake, we still return DateType as the timestamp type in resultSet metadata (#5723) + else if (type instanceof DateType) + type = TimestampType.instance; DataType dt = dataTypeMap.get(type); if (dt == null) http://git-wip-us.apache.org/repos/asf/cassandra/blob/9ec7b808/src/java/org/apache/cassandra/type/DateSerializer.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/type/DateSerializer.java b/src/java/org/apache/cassandra/type/DateSerializer.java deleted file mode 100644 index 4f0186c..0000000 --- a/src/java/org/apache/cassandra/type/DateSerializer.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.cassandra.type; - -import org.apache.cassandra.utils.ByteBufferUtil; - -import java.nio.ByteBuffer; -import java.text.SimpleDateFormat; -import java.util.Date; - -public class DateSerializer extends AbstractSerializer<Date> -{ - public static final String[] iso8601Patterns = new String[] { - "yyyy-MM-dd HH:mm", - "yyyy-MM-dd HH:mm:ss", - "yyyy-MM-dd HH:mmZ", - "yyyy-MM-dd HH:mm:ssZ", - "yyyy-MM-dd'T'HH:mm", - "yyyy-MM-dd'T'HH:mmZ", - "yyyy-MM-dd'T'HH:mm:ss", - "yyyy-MM-dd'T'HH:mm:ssZ", - "yyyy-MM-dd", - "yyyy-MM-ddZ" - }; - - static final String DEFAULT_FORMAT = iso8601Patterns[3]; - - static final ThreadLocal<SimpleDateFormat> FORMATTER = new ThreadLocal<SimpleDateFormat>() - { - protected SimpleDateFormat initialValue() - { - return new SimpleDateFormat(DEFAULT_FORMAT); - } - }; - - public static final DateSerializer instance = new DateSerializer(); - - @Override - public Date serialize(ByteBuffer bytes) - { - return bytes.remaining() > 0 - ? new Date(ByteBufferUtil.toLong(bytes)) - : null; - } - - @Override - public ByteBuffer deserialize(Date value) - { - return (value == null) - ? ByteBufferUtil.EMPTY_BYTE_BUFFER - : ByteBufferUtil.bytes(value.getTime()); - } - - @Override - public void validate(ByteBuffer bytes) throws MarshalException - { - if (bytes.remaining() != 8 && bytes.remaining() != 0) - throw new MarshalException(String.format("Expected 8 or 0 byte long for date (%d)", bytes.remaining())); - } - - @Override - public String getString(ByteBuffer bytes) - { - if (bytes.remaining() == 0) - { - return ""; - } - if (bytes.remaining() != 8) - { - throw new MarshalException("A date is exactly 8 bytes (stored as a long): " + bytes.remaining()); - } - - // uses ISO-8601 formatted string - return FORMATTER.get().format(new Date(ByteBufferUtil.toLong(bytes))); - } - - @Override - public String toString(Date value) - { - return FORMATTER.get().format(value); - } - - @Override - public Class<Date> getType() - { - return Date.class; - } -} http://git-wip-us.apache.org/repos/asf/cassandra/blob/9ec7b808/src/java/org/apache/cassandra/type/TimestampSerializer.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/type/TimestampSerializer.java b/src/java/org/apache/cassandra/type/TimestampSerializer.java new file mode 100644 index 0000000..84042e1 --- /dev/null +++ b/src/java/org/apache/cassandra/type/TimestampSerializer.java @@ -0,0 +1,104 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.cassandra.type; + +import org.apache.cassandra.utils.ByteBufferUtil; + +import java.nio.ByteBuffer; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class TimestampSerializer extends AbstractSerializer<Date> +{ + public static final String[] iso8601Patterns = new String[] { + "yyyy-MM-dd HH:mm", + "yyyy-MM-dd HH:mm:ss", + "yyyy-MM-dd HH:mmZ", + "yyyy-MM-dd HH:mm:ssZ", + "yyyy-MM-dd'T'HH:mm", + "yyyy-MM-dd'T'HH:mmZ", + "yyyy-MM-dd'T'HH:mm:ss", + "yyyy-MM-dd'T'HH:mm:ssZ", + "yyyy-MM-dd", + "yyyy-MM-ddZ" + }; + + static final String DEFAULT_FORMAT = iso8601Patterns[3]; + + static final ThreadLocal<SimpleDateFormat> FORMATTER = new ThreadLocal<SimpleDateFormat>() + { + protected SimpleDateFormat initialValue() + { + return new SimpleDateFormat(DEFAULT_FORMAT); + } + }; + + public static final TimestampSerializer instance = new TimestampSerializer(); + + @Override + public Date serialize(ByteBuffer bytes) + { + return bytes.remaining() > 0 + ? new Date(ByteBufferUtil.toLong(bytes)) + : null; + } + + @Override + public ByteBuffer deserialize(Date value) + { + return (value == null) + ? ByteBufferUtil.EMPTY_BYTE_BUFFER + : ByteBufferUtil.bytes(value.getTime()); + } + + @Override + public void validate(ByteBuffer bytes) throws MarshalException + { + if (bytes.remaining() != 8 && bytes.remaining() != 0) + throw new MarshalException(String.format("Expected 8 or 0 byte long for date (%d)", bytes.remaining())); + } + + @Override + public String getString(ByteBuffer bytes) + { + if (bytes.remaining() == 0) + { + return ""; + } + if (bytes.remaining() != 8) + { + throw new MarshalException("A date is exactly 8 bytes (stored as a long): " + bytes.remaining()); + } + + // uses ISO-8601 formatted string + return FORMATTER.get().format(new Date(ByteBufferUtil.toLong(bytes))); + } + + @Override + public String toString(Date value) + { + return FORMATTER.get().format(value); + } + + @Override + public Class<Date> getType() + { + return Date.class; + } +}