Skip to content

Commit 7cd0eab

Browse files
Support any number of trailing zeroes when decoding dates.
This was causing sporadic issues when the server returns dates that are valid according to the ISO standard, but not according to DateTime.ParseExact. Fixes #64.
1 parent 325a5ad commit 7cd0eab

File tree

7 files changed

+53
-9
lines changed

7 files changed

+53
-9
lines changed

Parse/Internal/FacebookAuthenticationProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public IDictionary<string, object> GetAuthData(string facebookId, string accessT
5656
return new Dictionary<string, object> {
5757
{"id", facebookId},
5858
{"access_token", accessToken},
59-
{"expiration_date", expiration.ToString(ParseClient.DateFormatString)}
59+
{"expiration_date", expiration.ToString(ParseClient.DateFormatStrings.First())}
6060
};
6161
}
6262

Parse/Internal/Installation/Controller/ParseCurrentInstallationController.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ public Task SetAsync(ParseInstallation installation, CancellationToken cancellat
4141
var data = installation.ServerDataToJSONObjectForSerialization();
4242
data["objectId"] = installation.ObjectId;
4343
if (installation.CreatedAt != null) {
44-
data["createdAt"] = installation.CreatedAt.Value.ToString(ParseClient.DateFormatString);
44+
data["createdAt"] = installation.CreatedAt.Value.ToString(ParseClient.DateFormatStrings.First());
4545
}
4646
if (installation.UpdatedAt != null) {
47-
data["updatedAt"] = installation.UpdatedAt.Value.ToString(ParseClient.DateFormatString);
47+
data["updatedAt"] = installation.UpdatedAt.Value.ToString(ParseClient.DateFormatStrings.First());
4848
}
4949

5050
ParseClient.ApplicationSettings["CurrentInstallation"] = Json.Encode(data);

Parse/Internal/ParseDecoder.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,9 @@ internal static DateTime ParseDate(string input) {
9797
// TODO(hallucinogen): Figure out if we should be more flexible with the date formats
9898
// we accept.
9999
return DateTime.ParseExact(input,
100-
ParseClient.DateFormatString,
101-
CultureInfo.InvariantCulture);
100+
ParseClient.DateFormatStrings,
101+
CultureInfo.InvariantCulture,
102+
DateTimeStyles.None);
102103
}
103104
}
104105
}

Parse/Internal/ParseEncoder.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System;
44
using System.Collections.Generic;
55
using System.Globalization;
6+
using System.Linq;
67

78
namespace Parse.Internal {
89
/// <summary>
@@ -31,7 +32,7 @@ public object Encode(object value) {
3132
// encoded object. Otherwise, just return the original object.
3233
if (value is DateTime) {
3334
return new Dictionary<string, object> {
34-
{"iso", ((DateTime)value).ToString(ParseClient.DateFormatString)},
35+
{"iso", ((DateTime)value).ToString(ParseClient.DateFormatStrings.First())},
3536
{"__type", "Date"}
3637
};
3738
}

Parse/Internal/User/Controller/ParseCurrentUserController.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
using System;
44
using System.Collections.Generic;
5+
using System.Linq;
56
using System.Threading;
67
using System.Threading.Tasks;
78

@@ -34,10 +35,10 @@ public Task SetAsync(ParseUser user, CancellationToken cancellationToken) {
3435
var data = user.ServerDataToJSONObjectForSerialization();
3536
data["objectId"] = user.ObjectId;
3637
if (user.CreatedAt != null) {
37-
data["createdAt"] = user.CreatedAt.Value.ToString(ParseClient.DateFormatString);
38+
data["createdAt"] = user.CreatedAt.Value.ToString(ParseClient.DateFormatStrings.First());
3839
}
3940
if (user.UpdatedAt != null) {
40-
data["updatedAt"] = user.UpdatedAt.Value.ToString(ParseClient.DateFormatString);
41+
data["updatedAt"] = user.UpdatedAt.Value.ToString(ParseClient.DateFormatStrings.First());
4142
}
4243

4344
ParseClient.ApplicationSettings["CurrentUser"] = Json.Encode(data);

Parse/ParseClient.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,16 @@ namespace Parse {
1818
/// configuration for the Parse library.
1919
/// </summary>
2020
public static partial class ParseClient {
21-
internal const string DateFormatString = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'";
21+
internal static readonly string[] DateFormatStrings = {
22+
// Official ISO format
23+
"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'Z'",
24+
25+
// It's possible that the string converter server-side may trim trailing zeroes,
26+
// so these two formats cover ourselves from that.
27+
"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'ff'Z'",
28+
"yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'f'Z'",
29+
};
30+
2231

2332
private static readonly object mutex = new object();
2433
private static readonly string[] assemblyNames = {

ParseTest.Unit/DecoderTests.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,38 @@ public void TestDecodeDate() {
5656
Assert.AreEqual(0, dateTime.Millisecond);
5757
}
5858

59+
[Test]
60+
public void TestDecodeImproperDate() {
61+
IDictionary<string, object> value = new Dictionary<string, object>() {
62+
{ "__type", "Date" },
63+
{ "iso", "1990-08-30T12:03:59.0Z" }
64+
};
65+
66+
DateTime dateTime = (DateTime)ParseDecoder.Instance.Decode(value);
67+
Assert.AreEqual(1990, dateTime.Year);
68+
Assert.AreEqual(8, dateTime.Month);
69+
Assert.AreEqual(30, dateTime.Day);
70+
Assert.AreEqual(12, dateTime.Hour);
71+
Assert.AreEqual(3, dateTime.Minute);
72+
Assert.AreEqual(59, dateTime.Second);
73+
Assert.AreEqual(0, dateTime.Millisecond);
74+
75+
// Test multiple trailing zeroes
76+
value = new Dictionary<string, object>() {
77+
{ "__type", "Date" },
78+
{ "iso", "1990-08-30T12:03:59.00Z" }
79+
};
80+
81+
dateTime = (DateTime)ParseDecoder.Instance.Decode(value);
82+
Assert.AreEqual(1990, dateTime.Year);
83+
Assert.AreEqual(8, dateTime.Month);
84+
Assert.AreEqual(30, dateTime.Day);
85+
Assert.AreEqual(12, dateTime.Hour);
86+
Assert.AreEqual(3, dateTime.Minute);
87+
Assert.AreEqual(59, dateTime.Second);
88+
Assert.AreEqual(0, dateTime.Millisecond);
89+
}
90+
5991
[Test]
6092
public void TestDecodeBytes() {
6193
IDictionary<string, object> value = new Dictionary<string, object>() {

0 commit comments

Comments
 (0)