title: How to write TIMESTAMP logical type (INT96) to parquet, using ParquetWriter?
body: <p>I have a tool that uses a <a href="https://github.com/apache/parquet-mr/blob/master/parquet-hadoop/src/main/java/org/apache/parquet/hadoop/ParquetWriter.java" rel="noreferrer">org.apache.parquet.hadoop.ParquetWriter</a> to convert CSV data files to parquet data files.</p>
  
  <p>Currently, it only handles <code>int32</code>, <code>double</code>, and <code>string</code></p>
  
  <p>I need to support the parquet <code>timestamp</code> logical type (annotated as int96), and I am lost on how to do that because I can't find a precise specification online.</p>
  
  <p>It appears this timestamp encoding (int96) is rare and not well supported. I've found very little specification details online. <a href="https://github.com/xhochy/parquet-format/blob/cb4727767823ae201fd567f67825cc22834c20e9/LogicalTypes.md#int96-timestamps-also-called-impala_timestamp" rel="noreferrer">This github README</a> states that:</p>
  
  <blockquote>
    <p>Timestamps saved as an int96 are made up of the nanoseconds in the day
    (first 8 byte) and the Julian day (last 4 bytes).</p>
  </blockquote>
  
  <p><strong>Specifically:</strong></p>
  
  <ol>
  <li>Which parquet <a href="https://github.com/apache/parquet-mr/blob/master/parquet-column/src/main/java/org/apache/parquet/schema/Type.java" rel="noreferrer">Type</a> do I use for the column in <a href="https://github.com/apache/parquet-mr/blob/master/parquet-column/src/main/java/org/apache/parquet/schema/MessageType.java" rel="noreferrer">MessageType</a> schema? I assume I should use the primitive type, <code>PrimitiveTypeName.INT96</code>, but I'm not sure if there may be a way to specify a logical type?</li>
  <li>How do I write the data? i.e. In what format do I write the timestamp to the group? For an INT96 timestamp, I assume I must write some binary type?</li>
  </ol>
  
  <p>Here is a simplified version of my code that demonstrates what I am trying to do. Specifically, take a look at the "TODO" comments, these are the two points in the code that correlate to the questions above.</p>
  
  <pre><code>List&lt;Type&gt; fields = new ArrayList&lt;&gt;();
  fields.add(new PrimitiveType(Type.Repetition.OPTIONAL, PrimitiveTypeName.INT32, "int32_col", null));
  fields.add(new PrimitiveType(Type.Repetition.OPTIONAL, PrimitiveTypeName.DOUBLE, "double_col", null));
  fields.add(new PrimitiveType(Type.Repetition.OPTIONAL, PrimitiveTypeName.STRING, "string_col", null));
  
  // TODO: 
  //   Specify the TIMESTAMP type. 
  //   How? INT96 primitive type? Is there a logical timestamp type I can use w/ MessageType schema?
  fields.add(new PrimitiveType(Type.Repetition.OPTIONAL, PrimitiveTypeName.INT96, "timestamp_col", null)); 
  
  MessageType schema = new MessageType("input", fields);
  
  // initialize writer
  Configuration configuration = new Configuration();
  configuration.setQuietMode(true);
  GroupWriteSupport.setSchema(schema, configuration);
  ParquetWriter&lt;Group&gt; writer = new ParquetWriter&lt;Group&gt;(
    new Path("output.parquet"),
    new GroupWriteSupport(),
    CompressionCodecName.SNAPPY,
    ParquetWriter.DEFAULT_BLOCK_SIZE,
    ParquetWriter.DEFAULT_PAGE_SIZE,
    1048576,
    true,
    false,
    ParquetProperties.WriterVersion.PARQUET_1_0,
    configuration
  );
  
  // write CSV data
  CSVParser parser = CSVParser.parse(new File(csv), StandardCharsets.UTF_8, CSVFormat.TDF.withQuote(null));
  ArrayList&lt;String&gt; columns = new ArrayList&lt;&gt;(schemaMap.keySet());
  int colIndex;
  int rowNum = 0;
  for (CSVRecord csvRecord : parser) {
    rowNum ++;
    Group group = f.newGroup();
    colIndex = 0;
    for (String record : csvRecord) {
      if (record == null || record.isEmpty() || record.equals( "NULL")) {
        colIndex++;
        continue;
      }
  
  
      record = record.trim();
      String type = schemaMap.get(columns.get(colIndex)).get("type").toString();
      MessageTypeConverter.addTypeValueToGroup(type, record, group, colIndex++);
  
      switch (colIndex) {
        case 0: // int32
          group.add(colIndex, Integer.parseInt(record));
          break;
        case 1: // double
          group.add(colIndex, Double.parseDouble(record));
          break;
        case 2: // string
          group.add(colIndex, record);
          break;
        case 3:
          // TODO: convert CSV string value to TIMESTAMP type (how?)
          throw new NotImplementedException();
      }
    }
    writer.write(group);
  }
  writer.close();
  </code></pre>