You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by br...@apache.org on 2020/08/13 21:18:59 UTC

[cassandra] branch trunk updated: Make TimestampSerializer accept fractional seconds of varying precision

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

brandonwilliams pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 517d3ec  Make TimestampSerializer accept fractional seconds of varying precision
517d3ec is described below

commit 517d3ecceee737dffa46c47c351a43092fd4a7ba
Author: Adam Holmberg <ad...@datastax.com>
AuthorDate: Fri Aug 7 13:24:12 2020 -0500

    Make TimestampSerializer accept fractional seconds of varying precision
    
    Patch by Adam Holmberg, reviewed by brandonwilliams for CASSANDRA-15976
---
 CHANGES.txt                                        |   1 +
 .../cassandra/serializers/TimestampSerializer.java | 149 +++++++------
 .../cql3/validation/entities/JsonTest.java         |   2 +-
 .../serializers/TimestampSerializerTest.java       | 231 +++++++++++++++++----
 4 files changed, 277 insertions(+), 106 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 7b4cc98..58239c9 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 4.0-beta2
+ * Make TimestampSerializer accept fractional seconds of varying precision (CASSANDRA-15976)
  * Improve cassandra-stress logging when using a profile file that doesn't exist (CASSANDRA-14425)
  * Improve logging for socket connection/disconnection (CASSANDRA-15980)
  * Throw FSWriteError upon write failures in order to apply DiskFailurePolicy (CASSANDRA-15928)
diff --git a/src/java/org/apache/cassandra/serializers/TimestampSerializer.java b/src/java/org/apache/cassandra/serializers/TimestampSerializer.java
index ac75d4b..49eb603 100644
--- a/src/java/org/apache/cassandra/serializers/TimestampSerializer.java
+++ b/src/java/org/apache/cassandra/serializers/TimestampSerializer.java
@@ -22,100 +22,108 @@ import org.apache.cassandra.utils.ByteBufferUtil;
 
 import java.nio.ByteBuffer;
 import java.text.SimpleDateFormat;
-import java.text.ParseException;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.time.format.DateTimeParseException;
+import java.time.temporal.ChronoField;
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.List;
 import java.util.TimeZone;
 import java.util.regex.Pattern;
 
-import org.apache.commons.lang3.time.DateUtils;
 
 public class TimestampSerializer implements TypeSerializer<Date>
 {
 
-    //NOTE: This list is used below and if you change the order
-    //      you need to update the default format and json formats in the code below.
-    private static final String[] dateStringPatterns = new String[]
+    private static final List<DateTimeFormatter> dateFormatters = generateFormatters();
+
+    private static List<DateTimeFormatter> generateFormatters()
     {
-            "yyyy-MM-dd HH:mm",
-            "yyyy-MM-dd HH:mm:ss",
-            "yyyy-MM-dd HH:mm z",
-            "yyyy-MM-dd HH:mm zz",
-            "yyyy-MM-dd HH:mm zzz",
-            "yyyy-MM-dd HH:mmX",
-            "yyyy-MM-dd HH:mmXX",  // DEFAULT_FORMAT
-            "yyyy-MM-dd HH:mmXXX",
-            "yyyy-MM-dd HH:mm:ss",
-            "yyyy-MM-dd HH:mm:ss z",
-            "yyyy-MM-dd HH:mm:ss zz",
-            "yyyy-MM-dd HH:mm:ss zzz",
-            "yyyy-MM-dd HH:mm:ssX",
-            "yyyy-MM-dd HH:mm:ssXX",
-            "yyyy-MM-dd HH:mm:ssXXX",
-            "yyyy-MM-dd HH:mm:ss.SSS",
-            "yyyy-MM-dd HH:mm:ss.SSS z",
-            "yyyy-MM-dd HH:mm:ss.SSS zz",
-            "yyyy-MM-dd HH:mm:ss.SSS zzz",
-            "yyyy-MM-dd HH:mm:ss.SSSX", // TO_JSON_FORMAT
-            "yyyy-MM-dd HH:mm:ss.SSSXX",
-            "yyyy-MM-dd HH:mm:ss.SSSXXX",
-            "yyyy-MM-dd'T'HH:mm",
-            "yyyy-MM-dd'T'HH:mm z",
-            "yyyy-MM-dd'T'HH:mm zz",
-            "yyyy-MM-dd'T'HH:mm zzz",
-            "yyyy-MM-dd'T'HH:mmX",
-            "yyyy-MM-dd'T'HH:mmXX",
-            "yyyy-MM-dd'T'HH:mmXXX",
-            "yyyy-MM-dd'T'HH:mm:ss",
-            "yyyy-MM-dd'T'HH:mm:ss z",
-            "yyyy-MM-dd'T'HH:mm:ss zz",
-            "yyyy-MM-dd'T'HH:mm:ss zzz",
-            "yyyy-MM-dd'T'HH:mm:ssX",
-            "yyyy-MM-dd'T'HH:mm:ssXX",
-            "yyyy-MM-dd'T'HH:mm:ssXXX",
-            "yyyy-MM-dd'T'HH:mm:ss.SSS",
-            "yyyy-MM-dd'T'HH:mm:ss.SSS z",
-            "yyyy-MM-dd'T'HH:mm:ss.SSS zz",
-            "yyyy-MM-dd'T'HH:mm:ss.SSS zzz",
-            "yyyy-MM-dd'T'HH:mm:ss.SSSX",  // UTC_FORMAT
-            "yyyy-MM-dd'T'HH:mm:ss.SSSXX",
-            "yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
-            "yyyy-MM-dd",
-            "yyyy-MM-dd z",
-            "yyyy-MM-dd zz",
-            "yyyy-MM-dd zzz",
-            "yyyy-MM-ddX",
-            "yyyy-MM-ddXX",
-            "yyyy-MM-ddXXX"
-    };
+        List<DateTimeFormatter> formatters = new ArrayList<>();
+
+        final String[] dateTimeFormats = new String[]
+                                         {
+                                         "yyyy-MM-dd'T'HH:mm[:ss]",
+                                         "yyyy-MM-dd HH:mm[:ss]"
+                                         };
+        final String[] offsetFormats = new String[]
+                                         {
+                                         " z",
+                                         "X",
+                                         " zzzz",
+                                         "XXX"
+                                         };
+
+        for (String dateTimeFormat: dateTimeFormats)
+        {
+            // local date time
+            formatters.add(
+            new DateTimeFormatterBuilder()
+            .appendPattern(dateTimeFormat)
+            .appendFraction(ChronoField.MILLI_OF_SECOND, 0, 9, true)
+            .toFormatter()
+            .withZone(ZoneId.systemDefault()));
+            for (String offset : offsetFormats)
+            {
+                formatters.add(
+                new DateTimeFormatterBuilder()
+                .appendPattern(dateTimeFormat)
+                .appendFraction(ChronoField.MILLI_OF_SECOND, 0, 9, true)
+                .appendPattern(offset)
+                .toFormatter()
+                );
+            }
+        }
+
+        for (String offset: offsetFormats)
+        {
+            formatters.add(
+            new DateTimeFormatterBuilder()
+            .appendPattern("yyyy-MM-dd")
+            .appendPattern(offset)
+            .parseDefaulting(ChronoField.NANO_OF_DAY, 0)
+            .toFormatter()
+            );
+        }
+
+        // local date
+        formatters.add(
+        new DateTimeFormatterBuilder()
+        .append(DateTimeFormatter.ISO_DATE)
+        .parseDefaulting(ChronoField.NANO_OF_DAY, 0)
+        .toFormatter().withZone(ZoneId.systemDefault()));
+
+        return formatters;
+    }
 
-    private static final String DEFAULT_FORMAT = dateStringPatterns[6];
     private static final Pattern timestampPattern = Pattern.compile("^-?\\d+$");
 
     private static final FastThreadLocal<SimpleDateFormat> FORMATTER = new FastThreadLocal<SimpleDateFormat>()
     {
         protected SimpleDateFormat initialValue()
         {
-            return new SimpleDateFormat(DEFAULT_FORMAT);
+            return new SimpleDateFormat("yyyy-MM-dd HH:mmXX");
         }
     };
 
-    private static final String UTC_FORMAT = dateStringPatterns[40];
     private static final FastThreadLocal<SimpleDateFormat> FORMATTER_UTC = new FastThreadLocal<SimpleDateFormat>()
     {
         protected SimpleDateFormat initialValue()
         {
-            SimpleDateFormat sdf = new SimpleDateFormat(UTC_FORMAT);
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
             sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
             return sdf;
         }
     };
 
-    private static final String TO_JSON_FORMAT = dateStringPatterns[19];
     private static final FastThreadLocal<SimpleDateFormat> FORMATTER_TO_JSON = new FastThreadLocal<SimpleDateFormat>()
     {
         protected SimpleDateFormat initialValue()
         {
-            SimpleDateFormat sdf = new SimpleDateFormat(TO_JSON_FORMAT);
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSX");
             sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
             return sdf;
         }
@@ -153,15 +161,18 @@ public class TimestampSerializer implements TypeSerializer<Date>
             }
         }
 
-        // Last chance, attempt to parse as date-time string
-        try
+        for (DateTimeFormatter fmt: dateFormatters)
         {
-            return DateUtils.parseDateStrictly(source, dateStringPatterns).getTime();
-        }
-        catch (ParseException e1)
-        {
-            throw new MarshalException(String.format("Unable to coerce '%s' to a formatted date (long)", source), e1);
+            try
+            {
+                return ZonedDateTime.parse(source, fmt).toInstant().toEpochMilli();
+            }
+            catch (DateTimeParseException e)
+            {
+                continue;
+            }
         }
+        throw new MarshalException(String.format("Unable to parse a date/time from '%s'", source));
     }
 
     public static SimpleDateFormat getJsonDateFormatter()
diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/JsonTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/JsonTest.java
index 71c1d62..e28e0cb 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/entities/JsonTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/entities/JsonTest.java
@@ -516,7 +516,7 @@ public class JsonTest extends CQLTester
         assertInvalidMessage("Expected a long or a datestring representation of a timestamp value, but got a Double",
                 "INSERT INTO %s (k, timestampval) VALUES (?, fromJson(?))", 0, "123.456");
 
-        assertInvalidMessage("Unable to coerce 'abcd' to a formatted date",
+        assertInvalidMessage("Unable to parse a date/time from 'abcd'",
                 "INSERT INTO %s (k, timestampval) VALUES (?, fromJson(?))", 0, "\"abcd\"");
 
         // ================ timeuuid ================
diff --git a/test/unit/org/apache/cassandra/serializers/TimestampSerializerTest.java b/test/unit/org/apache/cassandra/serializers/TimestampSerializerTest.java
index d991845..0cb365a 100644
--- a/test/unit/org/apache/cassandra/serializers/TimestampSerializerTest.java
+++ b/test/unit/org/apache/cassandra/serializers/TimestampSerializerTest.java
@@ -17,61 +17,220 @@
  * under the License.
  */
 package org.apache.cassandra.serializers;
-
+import java.util.Arrays;
 import java.util.List;
 import java.util.ArrayList;
 
 import org.junit.Test;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
-
-import org.apache.cassandra.serializers.MarshalException;
-import org.apache.cassandra.serializers.TimestampSerializer;
+import static org.junit.Assert.fail;
 
 public class TimestampSerializerTest
 {
-    private String dates[] = new String[]
+    public static final long ONE_SECOND = 1000L;
+    public static final long ONE_MINUTE = 60 * ONE_SECOND;
+    public static final long ONE_HOUR = 60 * ONE_MINUTE;
+    public static final long ONE_DAY = 24 * ONE_HOUR;
+    public static final long BASE_OFFSET = TimestampSerializer.dateStringToTimestamp("1970-01-01");
+
+    @Test
+    public void testFormatResults() throws MarshalException
+    {
+        validateStringTimestamp("1970-01-01 00:00", BASE_OFFSET);
+        validateStringTimestamp("1970-01-01 00:01", BASE_OFFSET + ONE_MINUTE);
+        validateStringTimestamp("1970-01-01 01:00", BASE_OFFSET + ONE_HOUR);
+        validateStringTimestamp("1970-01-02 00:00", BASE_OFFSET + ONE_DAY);
+        validateStringTimestamp("1970-01-02 00:00 UTC", ONE_DAY);
+        validateStringTimestamp("1970-01-01 00:01+01", ONE_MINUTE - ONE_HOUR);
+        validateStringTimestamp("1970-01-01 01:00+0100", ONE_HOUR - ONE_HOUR);
+        validateStringTimestamp("1970-01-02 00:00+01:00", ONE_DAY - ONE_HOUR);
+        validateStringTimestamp("1970-01-01 01:00-0200", ONE_HOUR + 2 * ONE_HOUR);
+        validateStringTimestamp("1970-01-01 01:00Z", ONE_HOUR);
+
+        validateStringTimestamp("1970-01-01 00:00:00", BASE_OFFSET);
+        validateStringTimestamp("1970-01-01 00:00:01", BASE_OFFSET + ONE_SECOND);
+        validateStringTimestamp("1970-01-01 00:01:00", BASE_OFFSET + ONE_MINUTE);
+        validateStringTimestamp("1970-01-01 01:00:00", BASE_OFFSET + ONE_HOUR);
+        validateStringTimestamp("1970-01-02 00:00:00", BASE_OFFSET + ONE_DAY);
+        validateStringTimestamp("1970-01-02 00:00:00 UTC", ONE_DAY);
+        validateStringTimestamp("1970-01-01 00:01:00+01", ONE_MINUTE - ONE_HOUR);
+        validateStringTimestamp("1970-01-01 01:00:00+0100", ONE_HOUR - ONE_HOUR);
+        validateStringTimestamp("1970-01-02 00:00:00+01:00", ONE_DAY - ONE_HOUR);
+        validateStringTimestamp("1970-01-01 01:00:00-0200", ONE_HOUR + 2 * ONE_HOUR);
+        validateStringTimestamp("1970-01-01 01:00:00Z", ONE_HOUR);
+
+        validateStringTimestamp("1970-01-01 00:00:00.000", BASE_OFFSET);
+        validateStringTimestamp("1970-01-01 00:00:00.000", BASE_OFFSET);
+        validateStringTimestamp("1970-01-01 00:00:01.000", BASE_OFFSET + ONE_SECOND);
+        validateStringTimestamp("1970-01-01 00:01:00.000", BASE_OFFSET + ONE_MINUTE);
+        validateStringTimestamp("1970-01-01 01:00:00.000", BASE_OFFSET + ONE_HOUR);
+        validateStringTimestamp("1970-01-02 00:00:00.000", BASE_OFFSET + ONE_DAY);
+        validateStringTimestamp("1970-01-02 00:00:00.000 UTC", ONE_DAY);
+        validateStringTimestamp("1970-01-01 00:00:00.100 UTC", 100L);
+        validateStringTimestamp("1970-01-01 00:01:00.001+01", ONE_MINUTE - ONE_HOUR + 1);
+        validateStringTimestamp("1970-01-01 01:00:00.002+0100", ONE_HOUR - ONE_HOUR + 2);
+        validateStringTimestamp("1970-01-02 00:00:00.003+01:00", ONE_DAY - ONE_HOUR + 3);
+        validateStringTimestamp("1970-01-01 01:00:00.004-0200", ONE_HOUR + 2 * ONE_HOUR + 4);
+        validateStringTimestamp("1970-01-01 01:00:00.004Z", ONE_HOUR + 4);
+
+        validateStringTimestamp("1970-01-01T00:00", BASE_OFFSET);
+        validateStringTimestamp("1970-01-01T00:01", BASE_OFFSET + ONE_MINUTE);
+        validateStringTimestamp("1970-01-01T01:00", BASE_OFFSET + ONE_HOUR);
+        validateStringTimestamp("1970-01-02T00:00", BASE_OFFSET + ONE_DAY);
+        validateStringTimestamp("1970-01-02T00:00 UTC", ONE_DAY);
+        validateStringTimestamp("1970-01-01T00:01+01", ONE_MINUTE - ONE_HOUR);
+        validateStringTimestamp("1970-01-01T01:00+0100", ONE_HOUR - ONE_HOUR);
+        validateStringTimestamp("1970-01-02T00:00+01:00", ONE_DAY - ONE_HOUR);
+        validateStringTimestamp("1970-01-01T01:00-0200", ONE_HOUR + 2 * ONE_HOUR);
+        validateStringTimestamp("1970-01-01T01:00Z", ONE_HOUR);
+
+        validateStringTimestamp("1970-01-01T00:00:00", BASE_OFFSET);
+        validateStringTimestamp("1970-01-01T00:00:01", BASE_OFFSET + ONE_SECOND);
+        validateStringTimestamp("1970-01-01T00:01:00", BASE_OFFSET + ONE_MINUTE);
+        validateStringTimestamp("1970-01-01T01:00:00", BASE_OFFSET + ONE_HOUR);
+        validateStringTimestamp("1970-01-02T00:00:00", BASE_OFFSET + ONE_DAY);
+        validateStringTimestamp("1970-01-02T00:00:00 UTC", ONE_DAY);
+        validateStringTimestamp("1970-01-01T00:01:00+01", ONE_MINUTE - ONE_HOUR);
+        validateStringTimestamp("1970-01-01T01:00:00+0100", ONE_HOUR - ONE_HOUR);
+        validateStringTimestamp("1970-01-02T00:00:00+01:00", ONE_DAY - ONE_HOUR);
+        validateStringTimestamp("1970-01-01T01:00:00-0200", ONE_HOUR + 2 * ONE_HOUR);
+        validateStringTimestamp("1970-01-01T01:00:00Z", ONE_HOUR);
+
+        validateStringTimestamp("1970-01-01T00:00:00.000", BASE_OFFSET);
+        validateStringTimestamp("1970-01-01T00:00:00.000", BASE_OFFSET);
+        validateStringTimestamp("1970-01-01T00:00:01.000", BASE_OFFSET + ONE_SECOND);
+        validateStringTimestamp("1970-01-01T00:01:00.000", BASE_OFFSET + ONE_MINUTE);
+        validateStringTimestamp("1970-01-01T01:00:00.000", BASE_OFFSET + ONE_HOUR);
+        validateStringTimestamp("1970-01-02T00:00:00.000", BASE_OFFSET + ONE_DAY);
+        validateStringTimestamp("1970-01-02T00:00:00.000 UTC", ONE_DAY);
+        validateStringTimestamp("1970-01-01T00:00:00.100 UTC", 100L);
+        validateStringTimestamp("1970-01-01T00:01:00.001+01", ONE_MINUTE - ONE_HOUR + 1);
+        validateStringTimestamp("1970-01-01T01:00:00.002+0100", ONE_HOUR - ONE_HOUR + 2);
+        validateStringTimestamp("1970-01-02T00:00:00.003+01:00", ONE_DAY - ONE_HOUR + 3);
+        validateStringTimestamp("1970-01-01T01:00:00.004-0200", ONE_HOUR + 2 * ONE_HOUR + 4);
+        validateStringTimestamp("1970-01-01T01:00:00.004Z", ONE_HOUR + 4);
+
+        validateStringTimestamp("1970-01-01", BASE_OFFSET);
+        validateStringTimestamp("1970-01-02 UTC", ONE_DAY);
+        validateStringTimestamp("1970-01-01+01", -ONE_HOUR);
+        validateStringTimestamp("1970-01-01+0100", -ONE_HOUR);
+        validateStringTimestamp("1970-01-02+01:00", ONE_DAY - ONE_HOUR);
+        validateStringTimestamp("1970-01-01-0200", 2 * ONE_HOUR);
+        validateStringTimestamp("1970-01-01Z", 0L);
+    }
+
+    @Test
+    public void testGeneralTimeZoneFormats()
     {
-        "2014-04-01",
-        "2014-04-01+0000",
-        "2014-04-01 20:30",
-        "2014-04-01 20:30:35",
-        "2014-04-01 20:30:35Z",
-        "2014-04-01 20:30+07",
-        "2014-04-01 20:30+0700",
-        "2014-04-01 20:30+07:00",
-        "2014-04-01 20:30:35+07",
-        "2014-04-01 20:30:35+0700",
-        "2014-04-01 20:30:35+07:00",
-        "2014-04-01 20:30:35.898",
-        "2014-04-01 20:30:35.898Z",
-        "2014-04-01 20:30:35.898+07",
-        "2014-04-01 20:30:35.898+0700",
-        "2014-04-01 20:30:35.898+07:00",
-        "2014-04-01T20:30",
-        "2014-04-01T20:30:25",
-        "2014-04-01T20:30:35Z",
-        "2014-04-01T20:30:35+00:00",
-        "2014-04-01T20:30:35+0700",
-        "2014-04-01T20:30:35+07:00",
-        "2014-04-01T20:30:35.898",
-        "2014-04-01T20:30:35.898+00:00"
-    };
+        // Central Standard Time; CST, GMT-06:00
+        final long cstOffset = 6 * ONE_HOUR;
+        validateStringTimestamp("1970-01-01 00:00:00 Central Standard Time", cstOffset);
+        validateStringTimestamp("1970-01-01 00:00:00 CST", cstOffset);
+        validateStringTimestamp("1970-01-01T00:00:00 GMT-06:00", cstOffset);
+    }
 
     @Test
-    public void testDateStringToTimestamp()
+    public void testVaryingFractionalPrecision() // CASSANDRA-15976
     {
-        List<String> unparsedDates = new ArrayList<>();
-        for (String date: dates)
+        validateStringTimestamp("1970-01-01 00:00:00.10 UTC", 100L);
+        validateStringTimestamp("1970-01-01 00:00:00.1+00", 100L);
+        validateStringTimestamp("1970-01-01T00:00:00.10-0000", 100L);
+        validateStringTimestamp("1970-01-01T00:00:00.1Z", 100L);
+        validateStringTimestamp("1970-01-01 00:00:00.1 Central Standard Time", 6 * ONE_HOUR + 100L);
+        validateStringTimestamp("1970-01-01 00:00:00.10+00:00", 100L);
+        validateStringTimestamp("1970-01-01T00:00:00.1", BASE_OFFSET + 100L);
+        validateStringTimestamp("1970-01-01T00:00:00.10-00:00", 100L);
+    }
+
+    @Test
+    public void testInvalidTimezones()
+    {
+        List<String> timestamps = new ArrayList<String>(
+            Arrays.asList(
+                "1970-01-01 00:00:00 xentral Standard Time",
+                "1970-01-01 00:00:00-060"
+            )
+        );
+        expectStringTimestampsExcept(timestamps);
+    }
+
+    @Test
+    public void testInvalidFormat()
+    {
+        List<String> timestamps = new ArrayList<String>(
+            Arrays.asList(
+                "1970-01-01 00:00:00andsomeextra",
+                "prefix1970-01-01 00:00:00",
+                "1970-01-01 00.56",
+                "1970-01-0100:00:00"
+            )
+        );
+        expectStringTimestampsExcept(timestamps);
+    }
+
+    @Test
+    public void testNumeric()
+    {
+        // now (positive
+        final long now = System.currentTimeMillis();
+        assertEquals(now, TimestampSerializer.dateStringToTimestamp(Long.toString(now)));
+
+        // negative
+        final long then = -11234L;
+        assertEquals(then, TimestampSerializer.dateStringToTimestamp(Long.toString(then)));
+
+        // overflows
+        for (Long l: Arrays.asList(Long.MAX_VALUE, Long.MIN_VALUE))
         {
             try
             {
-                long millis = TimestampSerializer.dateStringToTimestamp(date);
+                TimestampSerializer.dateStringToTimestamp(Long.toString(l) + "1");
+                fail("Expected exception was not raised while parsing overflowed long.");
             }
             catch (MarshalException e)
             {
-                unparsedDates.add(date);
+                continue;
             }
         }
-        assertTrue("Unable to parse: " + unparsedDates, unparsedDates.isEmpty());
+    }
+
+    @Test
+    public void testNow()
+    {
+        final long threshold = 5;
+        final long now = System.currentTimeMillis();
+        final long parsed = TimestampSerializer.dateStringToTimestamp("now");
+        assertTrue("'now' timestamp not within expected tolerance.", now <= parsed && parsed <= now + threshold);
+    }
+
+    private void validateStringTimestamp(String s, long expected)
+    {
+        try
+        {
+            long ts = TimestampSerializer.dateStringToTimestamp(s);
+            assertEquals( "Failed to parse expected timestamp value from " + s, expected, ts);
+        }
+        catch (MarshalException e)
+        {
+            fail(String.format("Failed to parse \"%s\" as timestamp.", s));
+        }
+    }
+
+    private void expectStringTimestampsExcept(List<String> timeStrings)
+    {
+        for (String s: timeStrings)
+        {
+            try
+            {
+                TimestampSerializer.dateStringToTimestamp(s);
+                fail(String.format("Parsing succeeded while expecting failure from \"%s\"", s));
+            }
+            catch(MarshalException e)
+            {
+                continue;
+            }
+
+        }
     }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org