]> source.dussan.org Git - jackcess.git/commitdiff
add TemporalConfig to allow for custom expression date/time formats
authorJames Ahlborn <jtahlborn@yahoo.com>
Sat, 14 Jan 2017 02:32:58 +0000 (02:32 +0000)
committerJames Ahlborn <jtahlborn@yahoo.com>
Sat, 14 Jan 2017 02:32:58 +0000 (02:32 +0000)
git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/branches/exprs@1079 f203690c-595d-4dc9-a70b-905162fa7fd2

src/main/java/com/healthmarketscience/jackcess/expr/EvalContext.java
src/main/java/com/healthmarketscience/jackcess/expr/TemporalConfig.java [new file with mode: 0644]
src/main/java/com/healthmarketscience/jackcess/impl/expr/BaseDelayedValue.java
src/main/java/com/healthmarketscience/jackcess/impl/expr/BaseNumericValue.java
src/main/java/com/healthmarketscience/jackcess/impl/expr/BuiltinOperators.java
src/main/java/com/healthmarketscience/jackcess/impl/expr/ExpressionTokenizer.java
src/main/java/com/healthmarketscience/jackcess/impl/expr/Expressionator.java
src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java

index 3ec3a883586efad5f39a97d6c4d6fb95b386792c..01a27b0ac7816189769a2ec09d59f3015540ab2d 100644 (file)
@@ -24,6 +24,8 @@ import java.text.SimpleDateFormat;
  */
 public interface EvalContext 
 {
+  public TemporalConfig getTemporalConfig();
+
   public Value.Type getResultType();
 
   public SimpleDateFormat createDateFormat(String formatStr);
diff --git a/src/main/java/com/healthmarketscience/jackcess/expr/TemporalConfig.java b/src/main/java/com/healthmarketscience/jackcess/expr/TemporalConfig.java
new file mode 100644 (file)
index 0000000..1c5bd74
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+Copyright (c) 2017 James Ahlborn
+
+Licensed 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 com.healthmarketscience.jackcess.expr;
+
+/**
+ *
+ * @author James Ahlborn
+ */
+public class TemporalConfig 
+{
+  public static final String US_DATE_FORMAT = "M/d/yyyy";
+  public static final String US_TIME_FORMAT_12 = "hh:mm:ss a";
+  public static final String US_TIME_FORMAT_24 = "HH:mm:ss";
+
+  public static final TemporalConfig US_TEMPORAL_CONFIG = new TemporalConfig(
+      US_DATE_FORMAT, US_TIME_FORMAT_12, US_TIME_FORMAT_24, '/', ':');
+
+  private final String _dateFormat;
+  private final String _timeFormat12;
+  private final String _timeFormat24;
+  private final char _dateSeparator;
+  private final char _timeSeparator;
+  private final String _dateTimeFormat12;
+  private final String _dateTimeFormat24;
+
+  public TemporalConfig(String dateFormat, String timeFormat12, 
+                        String timeFormat24, char dateSeparator, 
+                        char timeSeparator)
+  {
+    _dateFormat = dateFormat;
+    _timeFormat12 = timeFormat12;
+    _timeFormat24 = timeFormat24;
+    _dateSeparator = dateSeparator;
+    _timeSeparator = timeSeparator;
+    _dateTimeFormat12 = _dateFormat + " " + _timeFormat12;
+    _dateTimeFormat24 = _dateFormat + " " + _timeFormat24;
+  }
+
+  public String getDateFormat() {
+    return _dateFormat;
+  }
+
+  public String getTimeFormat12() {
+    return _timeFormat12;
+  }
+
+  public String getTimeFormat24() {
+    return _timeFormat24;
+  }
+
+  public String getDateTimeFormat12() {
+    return _dateTimeFormat12;
+  }
+
+  public String getDateTimeFormat24() {
+    return _dateTimeFormat24;
+  }
+
+  public String getDefaultDateFormat() {
+    return getDateFormat();
+  }
+
+  public String getDefaultTimeFormat() {
+    return getTimeFormat12();
+  }
+
+  public String getDefaultDateTimeFormat() {
+    return getDateTimeFormat12();
+  }
+
+  public char getDateSeparator() {
+    return _dateSeparator;
+  }
+
+  public char getTimeSeparator() {
+    return _timeSeparator;
+  }
+}
index c75f2b592f305c7426fefeb0f6561bdee9d03b9f..5ee037ca81159a62a2f3a3f5acd699678e1a01c1 100644 (file)
@@ -1,4 +1,18 @@
-// Copyright (c) 2016 Dell Boomi, Inc.
+/*
+Copyright (c) 2017 James Ahlborn
+
+Licensed 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 com.healthmarketscience.jackcess.impl.expr;
 
index 7f62b6aed10ec73caca411971fb7ef1c81237ace..fc136bf0a16a5bc9c1c619afe33b94548fc177e6 100644 (file)
@@ -1,8 +1,21 @@
-// Copyright (c) 2016 Dell Boomi, Inc.
+/*
+Copyright (c) 2017 James Ahlborn
+
+Licensed 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 com.healthmarketscience.jackcess.impl.expr;
 
-import com.healthmarketscience.jackcess.expr.Value;
 
 /**
  *
index 0329426818e05736512213b616955fa81c40136e..5bd6ac92b63fea2417a893f136ce098d043453d9 100644 (file)
@@ -600,13 +600,13 @@ public class BuiltinOperators
       String fmtStr = null;
       switch(type) {
       case DATE:
-        fmtStr = ExpressionTokenizer.DATE_FORMAT;
+        fmtStr = ctx.getTemporalConfig().getDefaultDateFormat();
         break;
       case TIME:
-        fmtStr = ExpressionTokenizer.TIME_FORMAT_24;
+        fmtStr = ctx.getTemporalConfig().getDefaultTimeFormat();
         break;
       case DATE_TIME:
-        fmtStr = ExpressionTokenizer.DATE_TIME_FORMAT_24;
+        fmtStr = ctx.getTemporalConfig().getDefaultDateTimeFormat();
         break;
       default:
         throw new RuntimeException("Unexpected type " + type);
index 46a816069b78f8a1028be618f45999b52f42c0c2..d078d448b1fd3948a29017aea3dcf1aa508c86d2 100644 (file)
@@ -16,7 +16,6 @@ limitations under the License.
 
 package com.healthmarketscience.jackcess.impl.expr;
 
-import java.math.BigDecimal;
 import java.text.DateFormat;
 import java.text.FieldPosition;
 import java.text.ParseException;
@@ -34,6 +33,7 @@ import java.util.TimeZone;
 
 import static com.healthmarketscience.jackcess.impl.expr.Expressionator.*;
 import com.healthmarketscience.jackcess.expr.Value;
+import com.healthmarketscience.jackcess.expr.TemporalConfig;
 
 
 /**
@@ -49,16 +49,12 @@ class ExpressionTokenizer
   private static final char DATE_LIT_QUOTE_CHAR = '#';
   private static final char EQUALS_CHAR = '=';
 
-  static final String DATE_FORMAT = "M/d/yyyy";
-  static final String TIME_FORMAT_24 = "HH:mm:ss";
-  static final String TIME_FORMAT_12 = "hh:mm:ss a";
-  static final String DATE_TIME_FORMAT_24 = DATE_FORMAT + " " + TIME_FORMAT_24;
-  static final String DATE_TIME_FORMAT_12 = DATE_FORMAT + " " + TIME_FORMAT_12;
   private static final int AMPM_SUFFIX_LEN = 3;
   private static final String AM_SUFFIX = " am";
   private static final String PM_SUFFIX = " pm";
   // access times are based on this date (not the UTC base)
   private static final String BASE_DATE = "12/30/1899 ";
+  private static final String BASE_DATE_FMT = "M/d/yyyy";
 
   private static final byte IS_OP_FLAG =     0x01;
   private static final byte IS_COMP_FLAG =   0x02;
@@ -321,10 +317,11 @@ class ExpressionTokenizer
 
   private static Token parseDateLiteralString(ExprBuf buf) 
   {
+    TemporalConfig cfg = buf.getTemporalConfig();
     String dateStr = parseStringUntil(buf, DATE_LIT_QUOTE_CHAR, null);
     
-    boolean hasDate = (dateStr.indexOf('/') >= 0);
-    boolean hasTime = (dateStr.indexOf(':') >= 0);
+    boolean hasDate = (dateStr.indexOf(cfg.getDateSeparator()) >= 0);
+    boolean hasTime = (dateStr.indexOf(cfg.getTimeSeparator()) >= 0);
     boolean hasAmPm = false;
     
     if(hasTime) {
@@ -451,6 +448,7 @@ class ExpressionTokenizer
     private DateFormat _dateTimeFmt12;
     private DateFormat _timeFmt24;
     private DateFormat _dateTimeFmt24;
+    private String _baseDate;
     private final StringBuilder _scratch = new StringBuilder();
     
     private ExprBuf(String str, ParseContext ctx) {
@@ -498,9 +496,13 @@ class ExpressionTokenizer
       return _scratch;
     }
 
+    public TemporalConfig getTemporalConfig() {
+      return _ctx.getTemporalConfig();
+    }
+
     public DateFormat getDateFormat() {
       if(_dateFmt == null) {
-        _dateFmt = _ctx.createDateFormat(DATE_FORMAT);
+        _dateFmt = _ctx.createDateFormat(getTemporalConfig().getDateFormat());
       }
       return _dateFmt;
     }
@@ -508,14 +510,17 @@ class ExpressionTokenizer
     public DateFormat getTimeFormat12() {
       if(_timeFmt12 == null) {
         _timeFmt12 = new TimeFormat(
-            getDateTimeFormat12(), _ctx.createDateFormat(TIME_FORMAT_12));
+            getDateTimeFormat12(), _ctx.createDateFormat(
+                getTemporalConfig().getTimeFormat12()),
+            getBaseDate());
       }
       return _timeFmt12;
     }
 
     public DateFormat getDateTimeFormat12() {
       if(_dateTimeFmt12 == null) {
-        _dateTimeFmt12 = _ctx.createDateFormat(DATE_TIME_FORMAT_12);
+        _dateTimeFmt12 = _ctx.createDateFormat(
+            getTemporalConfig().getDateTimeFormat12());
       }
       return _dateTimeFmt12;
     }
@@ -523,18 +528,39 @@ class ExpressionTokenizer
     public DateFormat getTimeFormat24() {
       if(_timeFmt24 == null) {
         _timeFmt24 = new TimeFormat(
-            getDateTimeFormat24(), _ctx.createDateFormat(TIME_FORMAT_24));
+            getDateTimeFormat24(), _ctx.createDateFormat(
+                getTemporalConfig().getTimeFormat24()),
+            getBaseDate());
       }
       return _timeFmt24;
     }
 
     public DateFormat getDateTimeFormat24() {
       if(_dateTimeFmt24 == null) {
-        _dateTimeFmt24 = _ctx.createDateFormat(DATE_TIME_FORMAT_24);
+        _dateTimeFmt24 = _ctx.createDateFormat(
+            getTemporalConfig().getDateTimeFormat24());
       }
       return _dateTimeFmt24;
     }
 
+    private String getBaseDate() {
+      if(_baseDate == null) {
+        String dateFmt = getTemporalConfig().getDateFormat();
+        String baseDate = BASE_DATE;
+        if(!BASE_DATE_FMT.equals(dateFmt)) {
+          try {
+            // need to reformat the base date to the relevant date format
+            DateFormat df = _ctx.createDateFormat(BASE_DATE_FMT);
+            baseDate = getDateFormat().format(df.parse(baseDate));
+          } catch(Exception e) {
+            throw new IllegalStateException("Could not parse base date", e);
+          }
+        }
+        _baseDate = baseDate + " ";
+      }
+      return _baseDate;
+    }
+
     @Override
     public String toString() {
       return "[char " + _pos + "] '" + _str + "'";
@@ -607,13 +633,17 @@ class ExpressionTokenizer
   private static final class TimeFormat extends DateFormat
   {
     private static final long serialVersionUID = 0L;
+
     private final DateFormat _parseDelegate;
     private final DateFormat _fmtDelegate;
+    private final String _baseDate;
 
-    private TimeFormat(DateFormat parseDelegate, DateFormat fmtDelegate)
+    private TimeFormat(DateFormat parseDelegate, DateFormat fmtDelegate, 
+                       String baseDate)
     {
       _parseDelegate = parseDelegate;
       _fmtDelegate = fmtDelegate;
+      _baseDate = baseDate;
     }
 
     @Override
@@ -625,7 +655,7 @@ class ExpressionTokenizer
     public Date parse(String source, ParsePosition pos) {
       // we parse as a full date/time in order to get the correct "base date"
       // used by access
-      return _parseDelegate.parse(BASE_DATE + source, pos);
+      return _parseDelegate.parse(_baseDate + source, pos);
     }
 
     @Override
index ab45edaa1c27c20270595b629394107e636d3ab7..c3732892d029db8528ce0667feae342a33b0d0b2 100644 (file)
@@ -37,6 +37,7 @@ import com.healthmarketscience.jackcess.DatabaseBuilder;
 import com.healthmarketscience.jackcess.expr.Expression;
 import com.healthmarketscience.jackcess.expr.Function;
 import com.healthmarketscience.jackcess.expr.EvalContext;
+import com.healthmarketscience.jackcess.expr.TemporalConfig;
 import com.healthmarketscience.jackcess.expr.Value;
 import com.healthmarketscience.jackcess.impl.expr.ExpressionTokenizer.Token;
 import com.healthmarketscience.jackcess.impl.expr.ExpressionTokenizer.TokenType;
@@ -62,11 +63,15 @@ public class Expressionator
   }
 
   public interface ParseContext {
+    public TemporalConfig getTemporalConfig();
     public SimpleDateFormat createDateFormat(String formatStr);
     public Function getExpressionFunction(String name);
   }
 
   public static final ParseContext DEFAULT_PARSE_CONTEXT = new ParseContext() {
+    public TemporalConfig getTemporalConfig() {
+      return TemporalConfig.US_TEMPORAL_CONFIG;
+    }
     public SimpleDateFormat createDateFormat(String formatStr) {
       return DatabaseBuilder.createDateFormat(formatStr);
     }
@@ -807,8 +812,8 @@ public class Expressionator
 
     case LIKE:
       Token t = buf.next();
-      // FIXME, create LITERAL_STRING TokenType?
-      if(t.getType() != TokenType.LITERAL) {
+      if((t.getType() != TokenType.LITERAL) || 
+         (t.getValueType() != Value.Type.STRING)) {
         throw new IllegalArgumentException("Missing Like pattern " + buf);
       }
       String patternStr = t.getValueStr();
index ffcf90140463313f4afa714ac5836ea1e4df9415..7f3fcdeafb91934d4e6e401992dcb33f8367f701 100644 (file)
@@ -22,6 +22,7 @@ import com.healthmarketscience.jackcess.DatabaseBuilder;
 import com.healthmarketscience.jackcess.TestUtil;
 import com.healthmarketscience.jackcess.expr.Expression;
 import com.healthmarketscience.jackcess.expr.Function;
+import com.healthmarketscience.jackcess.expr.TemporalConfig;
 import junit.framework.TestCase;
 
 /**
@@ -280,6 +281,9 @@ public class ExpressionatorTest extends TestCase
 
   private static final class TestContext implements Expressionator.ParseContext
   {
+    public TemporalConfig getTemporalConfig() {
+      return TemporalConfig.US_TEMPORAL_CONFIG;
+    }
     public SimpleDateFormat createDateFormat(String formatStr) {
       SimpleDateFormat sdf = DatabaseBuilder.createDateFormat(formatStr);
       sdf.setTimeZone(TestUtil.TEST_TZ);