<!-- # C# 日期型 JSON 序列化后毫秒部分的7位数字是啥? --> <!-- csharp-what-is-the-7-digits-millisecond-in-json-serialized-string-of-date-filed --> 使用 *Newtonsoft.Json* 来序列化日期型字段时,其毫秒部分有7为小数。一直也没太在意,但昨天在 Java 中使用 `SimpleDateFormat` 反序列化时却发现没有对应的格式说明符。 *Newtonsoft.Json* 序列化时默认使用了 `IsoDateTimeConverter`,其序列化后结果为 *2019-09-23T15:20:06.4086491+08:00*。 而 Java 中 `SimpleDateFormat` 最接近的一种格式为 `yyyy-MM-dd'T'HH:mm:ss.SSSXXX`,其毫秒部分只有3位小数,反序列化上面的日期字符串会报错。 为啥会失败?首先要搞清楚这7位数字是啥。请参考 MSDN 的文档: > ## The `"fffffff"` custom format specifier > > The `"fffffff"` custom format specifier represents the seven most significant digits of the seconds fraction; that is, it represents the ten millionths of a second in a date and time value. > > Although it's possible to display the ten millionths of a second component of a time value, that value may not be meaningful. The precision of date and time values depends on the resolution of the system clock. On the Windows NT 3.5 (and later) and Windows Vista operating systems, the clock's resolution is approximately 10-15 milliseconds. `"fffffff"` 表示千万分之一精度的秒,但是后面又说了,在 Windows 中其精度由系统时钟决定,大约在 10-15 毫秒。换句话说,在 Windows 中这个精度是几乎是没有意义的。(不知道在 Linux 中其精度是多少?) ## C# 中自定义日期型的序列化方法 既然几乎没意义,那么就可以放心的删掉了。参考 [这篇博客](https://blog.csdn.net/qq_31975127/article/details/90713081 "Newtonsoft.Json 日期格式化"),有两种方式可以修改日期型的序列化格式,具体使用哪种方式根据项目需要而定。 1. 在序列化时通过传递一个 `JsonSerializerSettings` 来配置日期的序列化格式 ```csharp JsonConvert.SerializeObject(obj, new JsonSerializerSettings() { DateFormatString = "yyyy-MM-dd'T'HH:mm:ss.fffzzz", }); ``` 2. 在需要自定义格式的日期字段上添加 `[JsonConverter(typeof(CustomDateTimeConverter))]` 特性 ```csharp public class CustomDateTimeConverter : IsoDateTimeConverter { public CustomDateTimeConverter() { DateTimeFormat = "yyyy-MM-dd'T'HH:mm:ss.fffzzz"; } } ``` ## Scala 中的反序列化处理 在 Java 中使用 `new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")` 的 `parse` 方法可以正确的转成 *Date* 型。 这里项目使用的 Genson 的 Scala 版的包。自定义 JSON 反序列化的方法如下: 1. 定义 *CustomGenson.scala* 类: ```scala import java.text.SimpleDateFormat import com.owlike.genson._ object CustomGenson { val customGenson = new ScalaGenson( new GensonBuilder() .useDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")) .withBundle(ScalaBundle().useOnlyConstructorFields(false)) .create() ) } ``` 2. 引用上面自定义的包,反序列化的方法不变。 ```scala import CustomGenson.customGenson._ fromJson[ModelName](strJson) ``` ## C# 中日期型格式化的格式说明符 摘自 [MSDN](https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings#fffffffSpecifier "Custom date and time format strings") | Format specifier | Description | Examples | | --------------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `"d"` | The day of the month, from 1 through 31. | *2009-06-01T13:45:30 -> 1*<br /> *2009-06-15T13:45:30 -> 15* | | `"dd"` | The day of the month, from 01 through 31. | *2009-06-01T13:45:30 -> 01*<br /> *2009-06-15T13:45:30 -> 15* | | `"ddd"` | The abbreviated name of the day of the week. | *2009-06-15T13:45:30 -> Mon (en-US)*<br /> *2009-06-15T13:45:30 -> Пн (ru-RU)*<br /> *2009-06-15T13:45:30 -> lun. (fr-FR)* | | `"dddd"` | The full name of the day of the week. | *2009-06-15T13:45:30 -> Monday (en-US)*<br /> *2009-06-15T13:45:30 -> понедельник (ru-RU)*<br /> *2009-06-15T13:45:30 -> lundi (fr-FR)* | | `"f"` | The tenths of a second in a date and time value. | *2009-06-15T13:45:30.6170000 -> 6*<br /> *2009-06-15T13:45:30.05 -> 0* | | `"ff"` | The hundredths of a second in a date and time value. | *2009-06-15T13:45:30.6170000 -> 61*<br /> *2009-06-15T13:45:30.0050000 -> 00* | | `"fff"` | The milliseconds in a date and time value. | *6/15/2009 13:45:30.617 -> 617*<br /> *6/15/2009 13:45:30.0005 -> 000* | | `"ffff"` | The ten thousandths of a second in a date and time value. | *2009-06-15T13:45:30.6175000 -> 6175*<br /> *2009-06-15T13:45:30.0000500 -> 0000* | | `"fffff"` | The hundred thousandths of a second in a date and time value. | *2009-06-15T13:45:30.6175400 -> 61754*<br /> *6/15/2009 13:45:30.000005 -> 00000* | | `"ffffff"` | The millionths of a second in a date and time value. | *2009-06-15T13:45:30.6175420 -> 617542*<br /> *2009-06-15T13:45:30.0000005 -> 000000* | | `"fffffff"` | The ten millionths of a second in a date and time value. | *2009-06-15T13:45:30.6175425 -> 6175425*<br /> *2009-06-15T13:45:30.0001150 -> 0001150* | | `"F"` | If non-zero, the tenths of a second in a date and time value. | *2009-06-15T13:45:30.6170000 -> 6*<br /> *2009-06-15T13:45:30.0500000 -> (no output)* | | `"FF"` | If non-zero, the hundredths of a second in a date and time value. | *2009-06-15T13:45:30.6170000 -> 61*<br /> *2009-06-15T13:45:30.0050000 -> (no output)* | | `"FFF"` | If non-zero, the milliseconds in a date and time value. | *2009-06-15T13:45:30.6170000 -> 617*<br /> *2009-06-15T13:45:30.0005000 -> (no output)* | | `"FFFF"` | If non-zero, the ten thousandths of a second in a date and time value. | *2009-06-15T13:45:30.5275000 -> 5275*<br /> *2009-06-15T13:45:30.0000500 -> (no output)* | | `"FFFFF"` | If non-zero, the hundred thousandths of a second in a date and time value. | *2009-06-15T13:45:30.6175400 -> 61754*<br /> *2009-06-15T13:45:30.0000050 -> (no output)* | | `"FFFFFF"` | If non-zero, the millionths of a second in a date and time value. | *2009-06-15T13:45:30.6175420 -> 617542*<br /> *2009-06-15T13:45:30.0000005 -> (no output)* | | `"FFFFFFF"` | If non-zero, the ten millionths of a second in a date and time value. | *2009-06-15T13:45:30.6175425 -> 6175425*<br /> *2009-06-15T13:45:30.0001150 -> 000115* | | `"g", "gg"` | The period or era. | *2009-06-15T13:45:30.6170000 -> A.D.* | | `"h"` | The hour, using a 12-hour clock from 1 to 12. | *2009-06-15T01:45:30 -> 1*<br /> *2009-06-15T13:45:30 -> 1* | | `"hh"` | The hour, using a 12-hour clock from 01 to 12. | *2009-06-15T01:45:30 -> 01*<br /> *2009-06-15T13:45:30 -> 01* | | `"H"` | The hour, using a 24-hour clock from 0 to 23. | *2009-06-15T01:45:30 -> 1*<br /> *2009-06-15T13:45:30 -> 13* | | `"HH"` | The hour, using a 24-hour clock from 00 to 23. | *2009-06-15T01:45:30 -> 01*<br /> *2009-06-15T13:45:30 -> 13* | | `"K"` | Time zone information. | *2009-06-15T13:45:30, Kind Unspecified ->*<br /> *2009-06-15T13:45:30, Kind Utc -> Z*<br /> *2009-06-15T13:45:30, Kind Local -> -07:00 (depends on local computer settings)*<br /> *2009-06-15T01:45:30-07:00 --> -07:00*<br /> *2009-06-15T08:45:30+00:00 --> +00:00* | | `"m"` | The minute, from 0 through 59. | *2009-06-15T01:09:30 -> 9*<br /> *2009-06-15T13:29:30 -> 29* | | `"mm"` | The minute, from 00 through 59. | *2009-06-15T01:09:30 -> 09*<br /> *2009-06-15T01:45:30 -> 45* | | `"M"` | The month, from 1 through 12. | *2009-06-15T13:45:30 -> 6* | | `"MM"` | The month, from 01 through 12. | *2009-06-15T13:45:30 -> 06* | | `"MMM"` | The abbreviated name of the month. | *2009-06-15T13:45:30 -> Jun (en-US)*<br /> *2009-06-15T13:45:30 -> juin (fr-FR)*<br /> *2009-06-15T13:45:30 -> Jun (zu-ZA)* | | `"MMMM"` | The full name of the month. | *2009-06-15T13:45:30 -> June (en-US)*<br /> *2009-06-15T13:45:30 -> juni (da-DK)*<br /> *2009-06-15T13:45:30 -> uJuni (zu-ZA)* | | `"s"` | The second, from 0 through 59. | *2009-06-15T13:45:09 -> 9* | | `"ss"` | The second, from 00 through 59. | *2009-06-15T13:45:09 -> 09* | | `"t"` | The first character of the AM/PM designator. | *2009-06-15T13:45:30 -> P (en-US)*<br /> *2009-06-15T13:45:30 -> 午 (ja-JP)*<br /> *2009-06-15T13:45:30 -> (fr-FR)* | | `"tt"` | The AM/PM designator. | *2009-06-15T13:45:30 -> PM (en-US)*<br /> *2009-06-15T13:45:30 -> 午後 (ja-JP)*<br /> *2009-06-15T13:45:30 -> (fr-FR)* | | `"y"` | The year, from 0 to 99. | *0001-01-01T00:00:00 -> 1*<br /> *0900-01-01T00:00:00 -> 0*<br /> *1900-01-01T00:00:00 -> 0*<br /> *2009-06-15T13:45:30 -> 9*<br /> *2019-06-15T13:45:30 -> 19* | | `"yy"` | The year, from 00 to 99. | *0001-01-01T00:00:00 -> 01*<br /> *0900-01-01T00:00:00 -> 00*<br /> *1900-01-01T00:00:00 -> 00*<br /> *2019-06-15T13:45:30 -> 19* | | `"yyy"` | The year, with a minimum of three digits. | *0001-01-01T00:00:00 -> 001*<br /> *0900-01-01T00:00:00 -> 900*<br /> *1900-01-01T00:00:00 -> 1900*<br /> *2009-06-15T13:45:30 -> 2009* | | `"yyyy"` | The year as a four-digit number. | *0001-01-01T00:00:00 -> 0001*<br /> *0900-01-01T00:00:00 -> 0900*<br /> *1900-01-01T00:00:00 -> 1900*<br /> *2009-06-15T13:45:30 -> 2009* | | `"yyyyy"` | The year as a five-digit number. | *0001-01-01T00:00:00 -> 00001*<br /> *2009-06-15T13:45:30 -> 02009* | | `"z"` | Hours offset from UTC, with no leading zeros. | *2009-06-15T13:45:30-07:00 -> -7* | | `"zz"` | Hours offset from UTC, with a leading zero for a single-digit value. | *2009-06-15T13:45:30-07:00 -> -07* | | `"zzz"` | Hours and minutes offset from UTC. | *2009-06-15T13:45:30-07:00 -> -07:00* | | `":"` | The time separator. | *2009-06-15T13:45:30 -> : (en-US)*<br /> *2009-06-15T13:45:30 -> . (it-IT)*<br /> *2009-06-15T13:45:30 -> : (ja-JP)* | | `"/"` | The date separator. | *2009-06-15T13:45:30 -> / (en-US)*<br /> *2009-06-15T13:45:30 -> - (ar-DZ)*<br /> *2009-06-15T13:45:30 -> . (tr-TR)* | | `"string"` | Literal string delimiter. | *2009-06-15T13:45:30 ("arr:" h:m t) -> arr: 1:45 P*<br /> *2009-06-15T13:45:30 ('arr:' h:m t) -> arr: 1:45 P* | | `%` | Defines the following character as a custom format specifier. | *2009-06-15T13:45:30 (%h) -> 1* | | `\` | The escape character. | *2009-06-15T13:45:30 (h \h) -> 1 h* | | `Any other character` | The character is copied to the result string unchanged. | *2009-06-15T01:45:30 (arr hh:mm t) -> arr 01:45 A* | ## Java 中日期型格式化的格式说明符 摘自 *SimpleDateFormat* 的注释。 | **Letter** | **Date or Time Component** | **Presentation** | **Examples** | | :--------: | ------------------------------------------------ | ------------------ | --------------------------------------- | | `G` | Era designator | Text | *AD* | | `y` | Year | Year | *1996; 96* | | `Y` | Week year | Year | *2009; 09* | | `M` | Month in year (context sensitive) | Month | *July; Jul; 07* | | `L` | Month in year (standalone form) | Month | *July; Jul; 07* | | `w` | Week in year | Number | *27* | | `W` | Week in month | Number | *2* | | `D` | Day in year | Number | *189* | | `d` | Day in month | Number | *10* | | `F` | Day of week in month | Number | *2* | | `E` | Day name in week | Text | *Tuesday; Tue* | | `u` | Day number of week (1 = Monday, ..., 7 = Sunday) | Number | *1* | | `a` | Am/pm marker | Text | *PM* | | `H` | Hour in day (0-23) | Number | *0* | | `k` | Hour in day (1-24) | Number | *24* | | `K` | Hour in am/pm (0-11) | Number | *0* | | `h` | Hour in am/pm (1-12) | Number | *12* | | `m` | Minute in hour | Number | *30* | | `s` | Second in minute | Number | *55* | | `S` | Millisecond | Number | *978* | | `z` | Time zone | General time zone | *Pacific Standard Time; PST; GMT-08:00* | | `Z` | Time zone | RFC 822 time zone | *\-0800* | | `X` | Time zone | ISO 8601 time zone | *\-08; -0800; -08:00* | ### **Examples** | **Date and Time Pattern** | **Result** | | -------------------------------- | -------------------------------------- | | `"yyyy.MM.dd G 'at' HH:mm:ss z"` | *2001.07.04 AD at 12:08:56 PDT* | | `"EEE, MMM d, ''yy"` | *Wed, Jul 4, '01* | | `"h:mm a"` | *12:08 PM* | | `"hh 'o''clock' a, zzzz"` | *12 o'clock PM, Pacific Daylight Time* | | `"K:mm a, z"` | *0:08 PM, PDT* | | `"yyyyy.MMMMM.dd GGG hh:mm aaa"` | *02001.July.04 AD 12:08 PM* | | `"EEE, d MMM yyyy HH:mm:ss Z"` | *Wed, 4 Jul 2001 12:08:56 -0700* | | `"yyMMddHHmmssZ"` | *010704120856-0700* | | `"yyyy-MM-dd'T'HH:mm:ss.SSSZ"` | *2001-07-04T12:08:56.235-0700* | | `"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"` | *2001-07-04T12:08:56.235-07:00* | | `"YYYY-'W'ww-u"` | *2001-W27-3* | Loading... 版权声明:本文为博主「佳佳」的原创文章,遵循 CC 4.0 BY-NC-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://www.liujiajia.me/2019/9/24/csharp-what-is-the-7-digits-millisecond-in-json-serialized-string-of-date-filed ← 上一篇 下一篇 → 提交