严格模式下的 MongoDB 日期格式

Posted

技术标签:

【中文标题】严格模式下的 MongoDB 日期格式【英文标题】:MongoDB date format in strict mode 【发布时间】:2016-06-24 13:21:57 【问题描述】:

    使用 MongoDB java 驱动程序,对 Document 应用 toJson() 方法将获得该文档的 JSON 表示,并将 JsonMode 设置为 STRICT。 以下纪元格式用于日期: "$date" : "dateAsMilliseconds"

    使用 mongoexport,我们得到了 ISO-8601 格式。

    见官方文档(https://docs.mongodb.com/manual/reference/mongodb-extended-json/):

    在严格模式下,日期是 ISO-8601 日期格式,在模板 YYYY-MM-DDTHH:mm:ss.mmm 之后有一个强制时区字段。

    MongoDB JSON 解析器目前不支持加载表示 Unix 纪元之前的日期的 ISO-8601 字符串。在格式化前纪元日期和超过系统 time_t 类型可以保存的日期时,使用以下格式: "$date" : "$numberLong" : "dateAsMilliseconds"

如果有人能解释一下为什么 MongoDB java 驱动程序、mongoexport 工具和官方文档之间没有通用格式,我将不胜感激?

谢谢。

【问题讨论】:

【参考方案1】:

很明显,Java 驱动程序偏离官方规范是有充分理由的。NO。唯一的例外是那些不能以 ISO8601 格式表示的日期(如 B.C. 日期...)

作为一种解决方法,我扩展了JsonWriter 类并提供了两个toJson 静态方法作为如何使用它的示例:

package whatever.package.you.like;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

import org.bson.BSONException;
import org.bson.BsonContextType;
import org.bson.BsonDocument;
import org.bson.codecs.BsonDocumentCodec;
import org.bson.codecs.EncoderContext;
import org.bson.conversions.Bson;
import org.bson.json.JsonMode;
import org.bson.json.JsonWriter;
import org.bson.json.JsonWriterSettings;

import com.mongodb.MongoClient;

/**
 * A @link JsonWriter extension that conforms to the "strict" JSON format
 * specified by MongoDB for data/time values.
 * 
 * The @link JsonWriter class provided in the MongoDB Java driver (version
 * 3.2.2) does not conform to official MongoDB specification for strict mode
 * JSON (see https://docs.mongodb.com/manual/reference/mongodb-extended-json/).
 * This is specifically a problem with the date/time values which get filled
 * with a milliseconds value (i.e. $date: 309249234098) instead of the ISO8601
 * date/time (i.e. $date: "2016-07-14T08:44:23.234Z") value which the
 * specification calls for. This extension of @link JsonWriter conforms to the
 * MongoDb specification in this regard.
 */
public class ConformingJsonWriter extends JsonWriter 
   private final JsonWriterSettings settings;

   private final Writer writer;

   private boolean writingIndentedDateTime = false;

   /**
    * Creates a new instance which uses @code writer to write JSON to.
    *
    * @param writer
    *           the writer to write JSON to.
    */
   public ConformingJsonWriter(final Writer writer) 
      this(writer, new JsonWriterSettings());
   

   /**
    * Creates a new instance which uses @code writer to write JSON to and uses
    * the given settings.
    *
    * @param writer
    *           the writer to write JSON to.
    * @param settings
    *           the settings to apply to this writer.
    */
   public ConformingJsonWriter(final Writer writer,
         final JsonWriterSettings settings) 
      super(writer, settings);
      this.writer = writer;
      this.settings = settings;
      setContext(new Context(null, BsonContextType.TOP_LEVEL, ""));
   

   private void writeIndentation(int skip) throws IOException 
      for (Context context = getContext()
            .getParentContext(); context != null; context = context
                  .getParentContext()) 
         if (skip-- <= 0) 
            writer.write(settings.getIndentCharacters());
         
      
   

   private static String millisToIso8601(long millis) throws IOException 
      SimpleDateFormat dateFormat = new SimpleDateFormat(
            "yyyy-MM-dd\'T\'HH:mm:ss.SSS\'Z\'");
      dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
      return dateFormat.format(new Date(millis));
   

   @Override
   protected void doWriteDateTime(final long value) 
      if ((settings.getOutputMode() == JsonMode.STRICT)
            && (value >= -59014396800000L && value <= 253399536000000L)) 
         try 
            writeStartDocument();
            if (settings.isIndent()) 
               writingIndentedDateTime = true;
               writer.write(settings.getNewLineCharacters());
               writeIndentation(0);
             else 
               writer.write(" ");
            
            writer.write("\"$date\" : ");
            writer.write("\"");
            writer.write(millisToIso8601(value));
            writer.write("\"");
            writeEndDocument();
            writingIndentedDateTime = false;
          catch (IOException e) 
            throw new BSONException("Wrapping IOException", e);
         
       else 
         super.doWriteDateTime(value);
      
   

   @Override
   protected void doWriteEndDocument() 
      if (writingIndentedDateTime) 
         try 
            writer.write(settings.getNewLineCharacters());
            writeIndentation(1);
            writer.write("");
            if (getContext()
                  .getContextType() == BsonContextType.SCOPE_DOCUMENT) 
               setContext(getContext().getParentContext());
               writeEndDocument();
             else 
               setContext(getContext().getParentContext());
            
          catch (IOException e) 
            throw new BSONException("Wrapping IOException", e);
         
       else 
         super.doWriteEndDocument();
      
   

   /**
    * Take a @link Bson instance and convert it to "strict" JSON
    * representation with no indentation (read, "NOT pretty printed").
    * 
    * @param bson
    *           The @link Bson instance to convert
    * @return The JSON representation.
    */
   public static String toJson(Bson bson) 
      return toJson(bson, new JsonWriterSettings());
   

   /**
    * Take a @link Bson instance and convert it to JSON representation.
    * 
    * @param bson
    *           The @link Bson instance to convert
    * @param writerSettings
    *           @link JsonWriterSettings that specify details about how the
    *           JSON output should look.
    * @return The JSON representation.
    */
   public static String toJson(Bson bson,
         final JsonWriterSettings writerSettings) 
      BsonDocumentCodec encoder = new BsonDocumentCodec();
      ConformingJsonWriter writer = new ConformingJsonWriter(new StringWriter(),
            writerSettings);
      encoder.encode(writer,
            bson.toBsonDocument(BsonDocument.class,
                  MongoClient.getDefaultCodecRegistry()),
            EncoderContext.builder().isEncodingCollectibleDocument(true)
                  .build());
      return writer.getWriter().toString();
   

【讨论】:

以上是关于严格模式下的 MongoDB 日期格式的主要内容,如果未能解决你的问题,请参考以下文章

严格模式下的 Angular 和本地存储

严格模式下的变量typeof对象?

严格模式

严格模式下的var和let的区别

js_递归函数在严格模式下的调用方法

Hive优化:严格模式