diff --git a/CHANGELOG.md b/CHANGELOG.md
index c25c710e731a0d045ab75a12cb70ee90531d3b1f..df5b85f849385868b6eb8e1d7daa605bc1b4e8d8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 * Basic caching for queries. The caching is enabled by default and can be
   controlled by the usual "cache" flag.
 * Documentation for the overall server structure.
+* Add `BEFORE`, `AFTER`, `UNTIL`, `SINCE` keywords for query transaction
 
 ### Changed
 
@@ -30,6 +31,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Fixed
 
+* #131 - CQL Parsing error when white space characters before some units.
+* #134 - CQL Parsing error when multiple white space characters after `FROM`.
 * #130 - Error during `FIND ENTITY` when
   `QUERY_FILTER_ENTITIES_WITHOUT_RETRIEVE_PERMISSIONS=False`.
 * #125 - `bend_symlinks` script did not allow whitespace in filename.
diff --git a/README.md b/README.md
index 21d5400e5383d4c2571f8a409f50cd72f926187a..62da38e86df18a34deddf7c31a651dd12d81b7e8 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,9 @@ when creating the merge request. This allows our team to work with you on your r
 - If you have a suggestion for the [documentation](https://docs.indiscale.com/caosdb-server/),
 the preferred way is also a merge request as describe above (the documentation resides in `src/doc`).
 However, you can also create an issue for it.
-- You can also contact us at **info (AT) caosdb.de**.
+- You can also contact us at **info (AT) caosdb.de** and join the
+  CaosDB community on
+  [#caosdb:matrix.org](https://matrix.to/#/!unwwlTfOznjEnMMXxf:matrix.org).
 
 ## License
 
diff --git a/doc/Query.md b/doc/Query.md
index 4ff07fbad5c8c6acffac9d6c57e325ed92c8a9cd..1d7748438cd7f1a4a84f0ab66215564cbc5ad36d 100644
--- a/doc/Query.md
+++ b/doc/Query.md
@@ -226,14 +226,13 @@ The following query returns entities which have a _pname1_ property with any val
 ### TransactionFilter
 
 *Definition*
- sugar:: `HAS BEEN` | `HAVE BEEN` | `HAD BEEN` | `WAS` | `IS` | 
+ sugar:: `HAS BEEN` | `HAVE BEEN` | `HAD BEEN` | `WAS` | `IS`
  negated_sugar:: `HAS NOT BEEN` | `HASN'T BEEN` | `WAS NOT` | `WASN'T` | `IS NOT` | `ISN'T`  | `HAVN'T BEEN` | `HAVE NOT BEEN` | `HADN'T BEEN` | `HAD NOT BEEN`
  by_clause:: `BY (ME | username | SOMEONE ELSE (BUT ME)? | SOMEONE ELSE BUT username)`
- date:: A date string of the form `YYYY-MM-DD`
- datetime:: A datetime string of the form `YYYY-MM-DD hh:mm:ss`
- time_clause:: `ON ($date|$datetime) ` Here is plenty of room for more syntactic sugar, e.g. a `TODAY` keyword, and more funcionality, e.g. ranges.  
+ datetime:: A datetime string of the form `YYYY[-MM[-DD(T| )[hh[:mm[:ss[.nnn][(+|-)zzzz]]]]]]`
+ time_clause:: `[AT|ON|IN|BEFORE|AFTER|UNTIL|SINCE] (datetime) `
 
-`FIND ename WHICH ($sugar|$negated_sugar)? (NOT)? (CREATED|INSERTED|UPDATED|DELETED) (by_clause time_clause?| time_clause by_clause?)`
+`FIND ename WHICH (sugar|negated_sugar)? (NOT)? (CREATED|INSERTED|UPDATED) (by_clause time_clause?| time_clause by_clause?)`
 
 *Examples*
 
@@ -247,8 +246,9 @@ The following query returns entities which have a _pname1_ property with any val
 
 `FIND ename WHICH HAS BEEN CREATED BY erwin`
 
-`FIND ename . CREATED BY erwin ON `
+`FIND ename WHICH HAS BEEN INSERTED SINCE 2021-04`
 
+Note that `SINCE` and `UNTIL` are inclusive, while `BEFORE` and `AFTER` are not.
 
 
 ### File Location
diff --git a/src/main/java/org/caosdb/server/query/CQLLexer.g4 b/src/main/java/org/caosdb/server/query/CQLLexer.g4
index 85061d30cacb222dd8c866d84a7abfb525a592a9..71b41d480bc171aa02af3b2e61eadd16345b3c6d 100644
--- a/src/main/java/org/caosdb/server/query/CQLLexer.g4
+++ b/src/main/java/org/caosdb/server/query/CQLLexer.g4
@@ -77,6 +77,22 @@ IN:
 	[Ii][Nn] WHITE_SPACE_f?
 ;
 
+AFTER:
+	[Aa][Ff][Tt][Ee][Rr] WHITE_SPACE_f?
+;
+
+BEFORE:
+    [Bb][Ee][Ff][Oo][Rr][Ee] WHITE_SPACE_f?
+;
+
+UNTIL:
+    [Uu][Nn][Tt][Ii][Ll] WHITE_SPACE_f?
+;
+
+SINCE:
+	[Ss][Ii][Nn][Cc][Ee] WHITE_SPACE_f?
+;
+
 IS_STORED_AT:
 	(IS_f WHITE_SPACE_f?)? [Ss][Tt][Oo][Rr][Ee][Dd] (WHITE_SPACE_f? AT)? WHITE_SPACE_f?
 ;
@@ -486,7 +502,7 @@ mode DOUBLE_QUOTE_MODE;
 mode SELECT_MODE;
 
     FROM:
-         [Ff][Rr][Oo][Mm]([ \t\n\r])? -> mode(DEFAULT_MODE)
+         [Ff][Rr][Oo][Mm]([ \t\n\r])* -> mode(DEFAULT_MODE)
     ;
 
     SELECT_DOT:
diff --git a/src/main/java/org/caosdb/server/query/CQLParser.g4 b/src/main/java/org/caosdb/server/query/CQLParser.g4
index 452321c670d4101bc603e657023143025f12c22f..6aea8c55f006cd9127d68f0c09e1654d9f9842a9 100644
--- a/src/main/java/org/caosdb/server/query/CQLParser.g4
+++ b/src/main/java/org/caosdb/server/query/CQLParser.g4
@@ -153,14 +153,15 @@ idfilter returns [IDFilter filter] locals [String o, String v, String a]
 	)?
 ;
 
-transaction returns [TransactionFilter filter] locals [String type, TransactionFilter.Transactor user, String time]
+transaction returns [TransactionFilter filter] locals [String type, TransactionFilter.Transactor user, String time, String time_op]
 @init{
 	$time = null;
 	$user = null;
 	$type = null;
+	$time_op = null;
 }
 @after{
-	$filter = new TransactionFilter($type,$user,$time);
+	$filter = new TransactionFilter($type,$user,$time,$time_op);
 }
 :
 	(
@@ -169,8 +170,8 @@ transaction returns [TransactionFilter filter] locals [String type, TransactionF
 	)
 	
 	(
-		transactor (transaction_time {$time = $transaction_time.tqp;})? {$user = $transactor.t;}
-		| transaction_time (transactor {$user = $transactor.t;})? {$time = $transaction_time.tqp;}
+		transactor (transaction_time {$time = $transaction_time.tqp; $time_op = $transaction_time.op;})? {$user = $transactor.t;}
+		| transaction_time (transactor {$user = $transactor.t;})? {$time = $transaction_time.tqp; $time_op = $transaction_time.op;}
 	)
 ;
 
@@ -199,12 +200,25 @@ username returns [Query.Pattern ep] locals [int type]
     ( STAR {$type = Query.Pattern.TYPE_LIKE;} | ~(STAR | WHITE_SPACE) )+
 ;
 
-transaction_time returns [String tqp]
+transaction_time returns [String tqp, String op]
+@init {
+     $op = "(";
+}
 :
+	(
+	    AT  {$op = "=";}
+        | (ON | IN)
+        | (
+        	BEFORE {$op = "<";}
+          	| UNTIL {$op = "<=";}
+          	| AFTER {$op = ">";}
+          	| SINCE {$op = ">=";}
+        )
+    )?
     (
-        (ON | IN)
-        (value {$tqp = $value.text;})
-    ) | TODAY {$tqp = TransactionFilter.TODAY;}
+        TODAY {$tqp = TransactionFilter.TODAY;}
+        | value {$tqp = $value.text;}
+    )
 ;
 
 /*
@@ -481,8 +495,10 @@ number_with_unit
 
 unit
 :
-	TXT
-	| NUM SLASH TXT
+	(~(WHITE_SPACE | WHICH | HAS_A | WITH | WHERE | DOT | AND | OR ))
+	(~(WHITE_SPACE))*
+	|
+	NUM SLASH (~(WHITE_SPACE))+
 ;
 
 location returns [String str]
diff --git a/src/main/java/org/caosdb/server/query/POV.java b/src/main/java/org/caosdb/server/query/POV.java
index a1008bd6a67a8712baf2c9b52ab164f619236263..b1a457529a0199edcc5061110ee97e416a264fff 100644
--- a/src/main/java/org/caosdb/server/query/POV.java
+++ b/src/main/java/org/caosdb/server/query/POV.java
@@ -70,8 +70,8 @@ public class POV implements EntityFilterInterface {
   private String propertiesTable = null;
   private String refIdsTable = null;
   private final HashMap<String, String> statistics = new HashMap<>();
-  private Logger logger = LoggerFactory.getLogger(getClass());
-  private Stack<String> prefix = new Stack<>();
+  private final Logger logger = LoggerFactory.getLogger(getClass());
+  private final Stack<String> prefix = new Stack<>();
 
   private Unit getUnit(final String s) throws ParserException {
     return CaosDBSystemOfUnits.getUnit(s);
@@ -116,7 +116,7 @@ public class POV implements EntityFilterInterface {
 
       // try and parse as integer
       try {
-        final Pattern dp = Pattern.compile("^(-?[0-9]++)([^(\\.[0-9])-][^-]*)?$");
+        final Pattern dp = Pattern.compile("^(-?[0-9]++)\\s*([^(\\.[0-9])-][^-]*)?$");
         final Matcher m = dp.matcher(value);
         if (!m.matches()) {
           throw new NumberFormatException();
@@ -133,7 +133,7 @@ public class POV implements EntityFilterInterface {
         this.vDouble = (double) this.vInt;
       } else {
         try {
-          final Pattern dp = Pattern.compile("^(-?[0-9]+(?:\\.[0-9]+))([^-]*)$");
+          final Pattern dp = Pattern.compile("^(-?[0-9]+(?:\\.[0-9]+))\\s*([^-]*)$");
           final Matcher m = dp.matcher(value);
           if (!m.matches()) {
             throw new NumberFormatException();
@@ -142,7 +142,7 @@ public class POV implements EntityFilterInterface {
           unitStr = m.group(2);
 
           this.vDouble = Double.parseDouble(vDoubleStr);
-          if ((this.vDouble % 1) == 0) {
+          if (this.vDouble % 1 == 0) {
             this.vInt = (int) Math.floor(this.vDouble);
           }
         } catch (final NumberFormatException e) {
@@ -509,14 +509,16 @@ public class POV implements EntityFilterInterface {
     return this.aggregate;
   }
 
-  private String measurement(String m) {
+  private String measurement(final String m) {
     return String.join("", prefix) + m;
   }
 
   @Override
   public String getCacheKey() {
-    StringBuilder sb = new StringBuilder();
-    if (this.getAggregate() != null) sb.append(this.aggregate);
+    final StringBuilder sb = new StringBuilder();
+    if (this.getAggregate() != null) {
+      sb.append(this.aggregate);
+    }
     sb.append(toString());
     if (this.hasSubProperty()) {
       sb.append(getSubProperty().getCacheKey());
diff --git a/src/main/java/org/caosdb/server/query/TransactionFilter.java b/src/main/java/org/caosdb/server/query/TransactionFilter.java
index fed4a7156048f1bd43f7e4000df80f29a38187a0..2099639b0fc862edb2fc0bceecfa17bad6dad167 100644
--- a/src/main/java/org/caosdb/server/query/TransactionFilter.java
+++ b/src/main/java/org/caosdb/server/query/TransactionFilter.java
@@ -29,7 +29,6 @@ import java.sql.Types;
 import org.caosdb.datetime.Date;
 import org.caosdb.datetime.DateTimeFactory2;
 import org.caosdb.datetime.Interval;
-import org.caosdb.datetime.SemiCompleteDateTime;
 import org.caosdb.datetime.UTCDateTime;
 import org.caosdb.server.accessControl.Principal;
 import org.caosdb.server.accessControl.UserSources;
@@ -96,14 +95,20 @@ public class TransactionFilter implements EntityFilterInterface {
     }
   }
 
-  public TransactionFilter(final String type, final Transactor transactor, final String time) {
+  public TransactionFilter(
+      final String type,
+      final Transactor transactor,
+      final String time,
+      final String timeOperator) {
     this.transactor = transactor;
     this.transactionTime = time;
     this.transactionType = type;
+    this.transactionTimeOperator = timeOperator;
   }
 
   private final Transactor transactor;
   private final String transactionTime;
+  private final String transactionTimeOperator;
   private final String transactionType;
 
   @Override
@@ -123,7 +128,7 @@ public class TransactionFilter implements EntityFilterInterface {
 
       } else {
         try {
-          dt = (SemiCompleteDateTime) DateTimeFactory2.valueOf(this.transactionTime);
+          dt = (Interval) DateTimeFactory2.valueOf(this.transactionTime);
         } catch (final ClassCastException e) {
           throw new QueryException("Transaction time must be a SemiCompleteDateTime.");
         } catch (final IllegalArgumentException e) {
@@ -201,14 +206,13 @@ public class TransactionFilter implements EntityFilterInterface {
           } else {
             prepareCall.setNull(10, Types.INTEGER);
           }
-          prepareCall.setString(11, "("); // '(' means 'is in the
           // interval'
         } else {
           prepareCall.setNull(9, Types.BIGINT);
           prepareCall.setNull(10, Types.INTEGER);
-          prepareCall.setNull(11, Types.CHAR);
         }
 
+        prepareCall.setString(11, transactionTimeOperator);
       } else {
         // ilb_sec, ilb_nanos, eub_sec, eub_nanos, operator_t
         prepareCall.setNull(7, Types.BIGINT);
@@ -251,6 +255,8 @@ public class TransactionFilter implements EntityFilterInterface {
     return "TRANS("
         + this.transactionType
         + ","
+        + this.transactionTimeOperator
+        + ","
         + this.transactionTime
         + ","
         + this.transactor
diff --git a/src/test/java/org/caosdb/server/query/TestCQL.java b/src/test/java/org/caosdb/server/query/TestCQL.java
index 2c28d7f59c018f90ce77e9f7691a362027655466..154977a909a9d34ac2034c1ef352b81e7b2a3cb7 100644
--- a/src/test/java/org/caosdb/server/query/TestCQL.java
+++ b/src/test/java/org/caosdb/server/query/TestCQL.java
@@ -237,6 +237,10 @@ public class TestCQL {
 
   String queryIssue31 = "FIND FILE WHICH IS STORED AT /data/in0.foo";
   String queryIssue116 = "FIND *";
+  String queryIssue132a = "FIND ENTITY WHICH HAS BEEN INSERTED AFTER TODAY";
+  String queryIssue132b = "FIND ENTITY WHICH HAS BEEN CREATED TODAY BY ME";
+  String queryIssue134 = "SELECT pname FROM  ename";
+  String queryIssue131 = "FIND ENTITY WITH pname = 13 €";
 
   // File paths ///////////////////////////////////////////////////////////////
   String filepath_verb01 = "/foo/";
@@ -5692,7 +5696,7 @@ public class TestCQL {
     System.out.println(sfq.toStringTree(parser));
 
     assertTrue(sfq.filter instanceof TransactionFilter);
-    assertEquals("TRANS(Insert,null,Transactor(some%,=))", sfq.filter.toString());
+    assertEquals("TRANS(Insert,null,null,Transactor(some%,=))", sfq.filter.toString());
   }
 
   /** String ticket242 = "FIND RECORD WHICH HAS been created by some.user"; */
@@ -5707,7 +5711,7 @@ public class TestCQL {
 
     System.out.println(sfq.toStringTree(parser));
 
-    assertEquals("TRANS(Insert,null,Transactor(some.user,=))", sfq.filter.toString());
+    assertEquals("TRANS(Insert,null,null,Transactor(some.user,=))", sfq.filter.toString());
     assertTrue(sfq.filter instanceof TransactionFilter);
   }
 
@@ -5781,7 +5785,7 @@ public class TestCQL {
     assertEquals("@(null,null)", n.getFilter().toString());
 
     assertTrue(f2.getLast() instanceof TransactionFilter);
-    assertEquals("TRANS(Insert,null,Transactor(null,=))", f2.getLast().toString());
+    assertEquals("TRANS(Insert,null,null,Transactor(null,=))", f2.getLast().toString());
   }
 
   /** String ticket262e = "COUNT FILE WHICH IS NOT REFERENCED AND WAS created by me"; */
@@ -5804,7 +5808,7 @@ public class TestCQL {
     assertEquals("@(null,null)", n.getFilter().toString());
 
     assertTrue(f2.getLast() instanceof TransactionFilter);
-    assertEquals("TRANS(Insert,null,Transactor(null,=))", f2.getLast().toString());
+    assertEquals("TRANS(Insert,null,null,Transactor(null,=))", f2.getLast().toString());
   }
 
   /** String ticket262f = "COUNT FILE WHICH IS NOT REFERENCED BY entity AND WAS created by me"; */
@@ -5827,7 +5831,7 @@ public class TestCQL {
     assertEquals("@(entity,null)", n.getFilter().toString());
 
     assertTrue(f2.getLast() instanceof TransactionFilter);
-    assertEquals("TRANS(Insert,null,Transactor(null,=))", f2.getLast().toString());
+    assertEquals("TRANS(Insert,null,null,Transactor(null,=))", f2.getLast().toString());
   }
 
   /**
@@ -5853,7 +5857,7 @@ public class TestCQL {
     assertEquals("@(entity,null)", n.getFilter().toString());
 
     assertTrue(f2.getLast() instanceof TransactionFilter);
-    assertEquals("TRANS(Insert,null,Transactor(null,=))", f2.getLast().toString());
+    assertEquals("TRANS(Insert,null,null,Transactor(null,=))", f2.getLast().toString());
   }
 
   /** String ticket262h = "COUNT FILE WHICH IS NOT REFERENCED BY entity WHICH WAS created by me"; */
@@ -5876,7 +5880,7 @@ public class TestCQL {
 
     assertNotNull(((Backreference) backref).getSubProperty());
     assertEquals(
-        "TRANS(Insert,null,Transactor(null,=))",
+        "TRANS(Insert,null,null,Transactor(null,=))",
         ((Backreference) backref).getSubProperty().getFilter().toString());
   }
 
@@ -5917,7 +5921,7 @@ public class TestCQL {
 
     assertNotNull(((Backreference) backref).getSubProperty());
     assertEquals(
-        "TRANS(Insert,null,Transactor(null,=))",
+        "TRANS(Insert,null,null,Transactor(null,=))",
         ((Backreference) backref).getSubProperty().getFilter().toString());
   }
 
@@ -6686,4 +6690,55 @@ public class TestCQL {
     assertEquals("POV(pname,=,with)", sfq.filter.toString());
     assertNull(((POV) sfq.filter).getSubProperty());
   }
+
+  @Test
+  /** String queryIssue132a = "FIND ENTITY WHICH HAS BEEN INSERTED AFTER TODAY"; */
+  public void testIssue132a() {
+    CQLLexer lexer;
+    lexer = new CQLLexer(CharStreams.fromString(this.queryIssue132a));
+    final CommonTokenStream tokens = new CommonTokenStream(lexer);
+
+    final CQLParser parser = new CQLParser(tokens);
+    final CqContext sfq = parser.cq();
+
+    System.out.println(sfq.toStringTree(parser));
+
+    assertEquals("TRANS(Insert,>,Today,null)", sfq.filter.toString());
+  }
+
+  @Test
+  /** String queryIssue132b = "FIND ENTITY WHICH HAS BEEN CREATED TODAY BY ME"; */
+  public void testIssue132b() {
+    CQLLexer lexer;
+    lexer = new CQLLexer(CharStreams.fromString(this.queryIssue132b));
+    final CommonTokenStream tokens = new CommonTokenStream(lexer);
+
+    final CQLParser parser = new CQLParser(tokens);
+    final CqContext sfq = parser.cq();
+
+    System.out.println(sfq.toStringTree(parser));
+
+    assertEquals("TRANS(Insert,(,Today,Transactor(null,=))", sfq.filter.toString());
+  }
+
+  /**
+   * Multiple white space chars after `FROM`.
+   *
+   * <p>String queryIssue134 = "SELECT pname FROM ename";
+   */
+  @Test
+  public void testIssue134() {
+    // must not throw ParsingException
+    new Query(this.queryIssue134).parse();
+  }
+  /**
+   * Space before special character unit
+   *
+   * <p>String queryIssue131= "FIND ENTITY WITH pname = 13 €";
+   */
+  @Test
+  public void testIssue131() {
+    // must not throw ParsingException
+    new Query(this.queryIssue131).parse();
+  }
 }