Skip to content

Commit eda6239

Browse files
author
Konstantina Chremmou
authored
Merge pull request #5802 from danilo-delbusso/bug/rfc-3339
CA-397409: Extend SDK deserialization support for xen-api dates
2 parents 08e1437 + dcfc0e4 commit eda6239

File tree

5 files changed

+227
-24
lines changed

5 files changed

+227
-24
lines changed

ocaml/sdk-gen/c/autogen/src/xen_common.c

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -950,7 +950,26 @@ static void parse_into(xen_session *s, xmlNode *value_node,
950950
{
951951
struct tm tm;
952952
memset(&tm, 0, sizeof(tm));
953-
strptime((char *)string, "%Y%m%dT%H:%M:%S", &tm);
953+
// We only support basic ISO8601 since the C SDK only
954+
// connects to the XML-RPC backend
955+
char *formats[] = {
956+
// no dashes, no colons
957+
"%Y%m%dT%H%M%S",
958+
// no dashes, with colons
959+
"%Y%m%dT%H:%M:%S",
960+
// dashes and colons
961+
"%Y-%m-%dT%H:%M:%S",
962+
};
963+
int num_formats = sizeof(formats) / sizeof(formats[0]);
964+
965+
for (int i = 0; i < num_formats; i++)
966+
{
967+
if (strptime((char *)string, formats[i], &tm) != NULL)
968+
{
969+
break;
970+
}
971+
}
972+
954973
((time_t *)value)[slot] = (time_t)mktime(&tm);
955974
free(string);
956975
}

ocaml/sdk-gen/csharp/autogen/src/Converters.cs

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -385,16 +385,54 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
385385

386386
internal class XenDateTimeConverter : IsoDateTimeConverter
387387
{
388-
private static readonly string[] DateFormatsUniversal =
389-
{
390-
"yyyyMMddTHH:mm:ssZ", "yyyy-MM-ddThh:mm:ssZ"
388+
string [] DateFormatsUtc = {
389+
// dashes and colons
390+
"yyyy-MM-ddTHH:mm:ssZ",
391+
"yyyy-MM-ddTHH:mm:ss.fffZ",
392+
393+
// no dashes, with colons
394+
"yyyyMMddTHH:mm:ssZ",
395+
"yyyyMMddTHH:mm:ss.fffZ",
396+
397+
// no dashes
398+
"yyyyMMddTHHmmssZ",
399+
"yyyyMMddTHHmmss.fffZ",
391400
};
392401

393-
private static readonly string[] DateFormatsOther =
402+
string[] DateFormatsLocal =
394403
{
395-
"yyyyMMddTHH:mm:ss",
404+
// no dashes
405+
"yyyyMMddTHHmmss.fffzzzz",
406+
"yyyyMMddTHHmmss.fffzzz",
407+
"yyyyMMddTHHmmss.fffzz",
408+
"yyyyMMddTHHmmss.fff",
409+
410+
"yyyyMMddTHHmmsszzzz",
396411
"yyyyMMddTHHmmsszzz",
397-
"yyyyMMddTHHmmsszz"
412+
"yyyyMMddTHHmmsszz",
413+
"yyyyMMddTHHmmss",
414+
415+
// no dashes, with colons
416+
"yyyyMMddTHH:mm:ss.fffzzzz",
417+
"yyyyMMddTHH:mm:ss.fffzzz",
418+
"yyyyMMddTHH:mm:ss.fffzz",
419+
"yyyyMMddTHH:mm:ss.fff",
420+
421+
"yyyyMMddTHH:mm:sszzzz",
422+
"yyyyMMddTHH:mm:sszzz",
423+
"yyyyMMddTHH:mm:sszz",
424+
"yyyyMMddTHH:mm:ss",
425+
426+
// dashes and colons
427+
"yyyy-MM-ddTHH:mm:ss.fffzzzz",
428+
"yyyy-MM-ddTHH:mm:ss.fffzzz",
429+
"yyyy-MM-ddTHH:mm:ss.fffzz",
430+
"yyyy-MM-ddTHH:mm:ss.fff",
431+
432+
"yyyy-MM-ddTHH:mm:sszzzz",
433+
"yyyy-MM-ddTHH:mm:sszzz",
434+
"yyyy-MM-ddTHH:mm:sszz",
435+
"yyyy-MM-ddTHH:mm:ss",
398436
};
399437

400438
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
@@ -403,11 +441,11 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
403441

404442
DateTime result;
405443

406-
if (DateTime.TryParseExact(str, DateFormatsUniversal, CultureInfo.InvariantCulture,
444+
if (DateTime.TryParseExact(str, DateFormatsUtc, CultureInfo.InvariantCulture,
407445
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out result))
408446
return result;
409447

410-
if (DateTime.TryParseExact(str, DateFormatsOther, CultureInfo.InvariantCulture,
448+
if (DateTime.TryParseExact(str, DateFormatsLocal, CultureInfo.InvariantCulture,
411449
DateTimeStyles.None, out result))
412450
return result;
413451

@@ -420,7 +458,7 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s
420458
{
421459
var dateTime = (DateTime)value;
422460
dateTime = dateTime.ToUniversalTime();
423-
var text = dateTime.ToString(DateFormatsUniversal[0], CultureInfo.InvariantCulture);
461+
var text = dateTime.ToString(DateFormatsUtc[0], CultureInfo.InvariantCulture);
424462
writer.WriteValue(text);
425463
return;
426464
}

ocaml/sdk-gen/go/templates/ConvertTime.mustache

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,32 @@
11
{{#serialize}}
2-
var timeFormats = []string{time.RFC3339, "20060102T15:04:05Z", "20060102T15:04:05"}
2+
var timeFormats = []string{
3+
time.RFC3339,
4+
"2006-01-02T15:04:05",
5+
6+
// no dashes, no colons
7+
"20060102T15:04:05Z",
8+
"20060102T15:04:05",
9+
"20060102T150405.999999999Z0700",
10+
"20060102T150405",
11+
"20060102T150405Z07",
12+
"20060102T150405Z07:00",
13+
14+
// no dashes, with colons
15+
"20060102T15:04:05Z07",
16+
"20060102T15:04:05Z0700",
17+
"20060102T15:04:05Z07:00",
18+
"20060102T15:04:05.999999999Z07",
19+
"20060102T15:04:05.999999999Z07:00",
20+
"20060102T15:04:05.999999999Z07",
21+
22+
// dashes and colon patterns not covered by `time.RFC3339`
23+
"2006-01-02T15:04:05Z07",
24+
"2006-01-02T15:04:05Z0700",
25+
"2006-01-02T15:04:05Z07:00",
26+
"2006-01-02T15:04:05.999999999Z07",
27+
"2006-01-02T15:04:05.999999999Z07:00",
28+
"2006-01-02T15:04:05.999999999Z07",
29+
}
330

431
//nolint:unparam
532
func serialize{{func_name_suffix}}(context string, value {{type}}) (string, error) {

ocaml/sdk-gen/go/test_data/time_convert.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,31 @@
1-
var timeFormats = []string{time.RFC3339, "20060102T15:04:05Z", "20060102T15:04:05"}
1+
var timeFormats = []string{
2+
time.RFC3339,
3+
"2006-01-02T15:04:05",
4+
5+
// no dashes, no colons
6+
"20060102T15:04:05Z",
7+
"20060102T15:04:05",
8+
"20060102T150405.999999999Z0700",
9+
"20060102T150405",
10+
"20060102T150405Z07",
11+
"20060102T150405Z07:00",
12+
13+
// no dashes, with colons
14+
"20060102T15:04:05Z07",
15+
"20060102T15:04:05Z0700",
16+
"20060102T15:04:05Z07:00",
17+
"20060102T15:04:05.999999999Z07",
18+
"20060102T15:04:05.999999999Z07:00",
19+
"20060102T15:04:05.999999999Z07",
20+
21+
// dashes and colon patterns not covered by `time.RFC3339`
22+
"2006-01-02T15:04:05Z07",
23+
"2006-01-02T15:04:05Z0700",
24+
"2006-01-02T15:04:05Z07:00",
25+
"2006-01-02T15:04:05.999999999Z07",
26+
"2006-01-02T15:04:05.999999999Z07:00",
27+
"2006-01-02T15:04:05.999999999Z07",
28+
}
229

330
//nolint:unparam
431
func serializeTime(context string, value time.Time) (string, error) {

ocaml/sdk-gen/java/autogen/xen-api/src/main/java/com/xensource/xenapi/CustomDateDeserializer.java

Lines changed: 104 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,97 @@
3737
import java.text.ParseException;
3838
import java.text.SimpleDateFormat;
3939
import java.util.Date;
40+
import java.util.TimeZone;
4041

4142
/**
42-
* {@link CustomDateDeserializer} is a Jackson JSON deserializer for parsing {@link Date} objects
43+
* {@link CustomDateDeserializer} is a Jackson JSON deserializer for parsing
44+
* {@link Date} objects
4345
* from custom date formats used in Xen-API responses.
4446
*/
4547
public class CustomDateDeserializer extends StdDeserializer<Date> {
4648

4749
/**
48-
* Array of {@link SimpleDateFormat} objects representing the custom date formats
49-
* used in XenServer API responses.
50+
* Array of {@link SimpleDateFormat} objects representing the date formats
51+
* used in xen-api responses.
52+
*
53+
* RFC-3339 date formats can be returned in either Zulu or time zone agnostic.
54+
* This list is not an exhaustive list of formats supported by RFC-3339, rather
55+
* a set of formats that will enable the deserialization of xen-api dates.
56+
* Formats are listed in order of decreasing precision. When adding
57+
* to this list, please ensure the order is kept.
5058
*/
51-
private final SimpleDateFormat[] dateFormatters
52-
= new SimpleDateFormat[]{
59+
private static final SimpleDateFormat[] dateFormatsUtc = {
60+
// Most commonly returned formats
61+
new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"),
62+
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"),
63+
new SimpleDateFormat("ss.SSS"),
64+
65+
// Other
66+
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),
5367
new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss'Z'"),
54-
new SimpleDateFormat("ss.SSS")
68+
new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss.SSS'Z'"),
69+
new SimpleDateFormat("yyyyMMdd'T'HHmmss.SSS'Z'"),
70+
71+
};
72+
73+
/**
74+
* Array of {@link SimpleDateFormat} objects representing the date formats for
75+
* local time.
76+
* These formats are used to parse dates in local time zones.
77+
* Formats are listed in order of decreasing precision. When adding
78+
* to this list, please ensure the order is kept.
79+
*/
80+
private static final SimpleDateFormat[] dateFormatsLocal = {
81+
// no dashes, no colons
82+
new SimpleDateFormat("yyyyMMdd'T'HHmmss.SSSZZZ"),
83+
new SimpleDateFormat("yyyyMMdd'T'HHmmss.SSSZZ"),
84+
new SimpleDateFormat("yyyyMMdd'T'HHmmss.SSSZ"),
85+
new SimpleDateFormat("yyyyMMdd'T'HHmmss.SSSXXX"),
86+
new SimpleDateFormat("yyyyMMdd'T'HHmmss.SSSXX"),
87+
new SimpleDateFormat("yyyyMMdd'T'HHmmss.SSSX"),
88+
new SimpleDateFormat("yyyyMMdd'T'HHmmss.SSS"),
89+
90+
new SimpleDateFormat("yyyyMMdd'T'HHmmssZZZ"),
91+
new SimpleDateFormat("yyyyMMdd'T'HHmmssZZ"),
92+
new SimpleDateFormat("yyyyMMdd'T'HHmmssZ"),
93+
new SimpleDateFormat("yyyyMMdd'T'HHmmssXXX"),
94+
new SimpleDateFormat("yyyyMMdd'T'HHmmssXX"),
95+
new SimpleDateFormat("yyyyMMdd'T'HHmmssX"),
96+
new SimpleDateFormat("yyyyMMdd'T'HHmmss"),
97+
98+
// no dashes, with colons
99+
new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss.SSSZZZ"),
100+
new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss.SSSZZ"),
101+
new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss.SSSZ"),
102+
new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss.SSSXXX"),
103+
new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss.SSSXX"),
104+
new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss.SSSX"),
105+
new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss.SSS"),
106+
107+
new SimpleDateFormat("yyyyMMdd'T'HH:mm:ssZZZ"),
108+
new SimpleDateFormat("yyyyMMdd'T'HH:mm:ssZZ"),
109+
new SimpleDateFormat("yyyyMMdd'T'HH:mm:ssZ"),
110+
new SimpleDateFormat("yyyyMMdd'T'HH:mm:ssXXX"),
111+
new SimpleDateFormat("yyyyMMdd'T'HH:mm:ssXX"),
112+
new SimpleDateFormat("yyyyMMdd'T'HH:mm:ssX"),
113+
new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss"),
114+
115+
// dashes and colons
116+
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZZ"),
117+
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZ"),
118+
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"),
119+
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"),
120+
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXX"),
121+
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX"),
122+
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"),
123+
124+
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZ"),
125+
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZ"),
126+
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"),
127+
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"),
128+
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXX"),
129+
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX"),
130+
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"),
55131
};
56132

57133
/**
@@ -62,28 +138,44 @@ public CustomDateDeserializer() {
62138
}
63139

64140
/**
65-
* Constructs a {@link CustomDateDeserializer} instance with the specified value type.
141+
* Constructs a {@link CustomDateDeserializer} instance with the specified value
142+
* type.
66143
*
67144
* @param t The value type to handle (can be null, handled by superclass)
68145
*/
69146
public CustomDateDeserializer(Class t) {
70147
super(t);
148+
var utcTimeZone = TimeZone.getTimeZone("UTC");
149+
for (var utcFormatter : dateFormatsUtc) {
150+
utcFormatter.setTimeZone(utcTimeZone);
151+
}
71152
}
72153

154+
private static
155+
73156
/**
74157
* Deserializes a {@link Date} object from the given JSON parser.
75158
*
76-
* @param jsonParser The JSON parser containing the date value to deserialize
159+
* @param jsonParser The JSON parser containing the date value to
160+
* deserialize
77161
* @param deserializationContext The deserialization context
78162
* @return The deserialized {@link Date} object
79163
* @throws IOException if an I/O error occurs during deserialization
80164
*/
81-
@Override
82-
public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
165+
@Override public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
166+
throws IOException {
167+
var text = jsonParser.getText();
168+
for (SimpleDateFormat formatter : dateFormatsUtc) {
169+
try {
170+
return formatter.parse(text);
171+
} catch (ParseException e) {
172+
// ignore
173+
}
174+
}
83175

84-
for (SimpleDateFormat formatter : dateFormatters) {
176+
for (SimpleDateFormat formatter : dateFormatsLocal) {
85177
try {
86-
return formatter.parse(jsonParser.getText());
178+
return formatter.parse(text);
87179
} catch (ParseException e) {
88180
// ignore
89181
}

0 commit comments

Comments
 (0)