-
Notifications
You must be signed in to change notification settings - Fork 863
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix DynamoDbv2 bug for incorrect DateTime epoch serialization when date falls out of epoch supported range #3672
Conversation
f64cf2c
to
b7fb176
Compare
|
||
if (dateTime.HasValue) | ||
{ | ||
entry = (Primitive)(dateTime.Value); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we want to modify the caller's reference of entry shouldn't we do something like:
entry = new Primitive(datetime.Value)
Aren't we just modifying the local copy of the reference here? I see that being done below, but maybe you forgot that here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was copied from the existing EpochSecondsToDateTime.
Yes, we are just modifying the local copy of the reference here and then returning it. entry = new Primitive(datetime.Value)
would also the same, but in a more elegant way. 😄
The function DateTimeToEpochSecondsLong()
was added by customer which uses new Primitive(epochSecondsAsString, saveAsNumeric: true)
, so didn't touched it.
@@ -822,7 +835,7 @@ private static void PopulateConfigFromType(ItemStorageConfig config, Type type) | |||
} | |||
} | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: added whitespace?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed it somehow.
|
||
// To prevent the data from being (de)serialized incorrectly, we choose to throw the exception instead | ||
// of swallowing it. | ||
throw; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should throw our own exception calling out our failure to convert the DateTime
to a Epoch. Provide the value of the DateTime, attributeName
, and the original exception as the inner exception. If we don't users will get a random numeric conversion exception and have no context why they are getting the error and how to fix it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Please review.
throw; | ||
} | ||
|
||
if (epochSecondsAsString != null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can never be false anymore since we are always throwing an exception if we fail to convert. To avoid the code getting refactored in weird ways in the future I would add line 336 right after the ConvertToUnixEpochSecondsString
call and then return. That would leave no code after the catch block and we don't need the extra if check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Please review.
@@ -346,6 +354,7 @@ public void TestContext_CustomDateTimeConverter(bool retrieveDateTimeInUtc) | |||
Context.ConverterCache.Add(typeof(DateTime), new DateTimeUtcConverter()); | |||
|
|||
var currTime = DateTime.Now; | |||
var longEpochTime = new DateTime(2039, 2, 5, 17, 49, 55, DateTimeKind.Local); // DateTime.Now returns Local kind. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test that uses a date before 1970 the start of the Unix epoch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Used var currTimeBefore1970 = new DateTime(1969, 6, 12, 4, 56, 43, DateTimeKind.Local);
for attribute EpochDate2
(it was somehow not decorated with StoreAsEpoch
. Decorated it with StoreAsEpoc
.
Please advise if we need to also test it with StoreAsEpochLong
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMPORTANT: We need to ensure if this PR is merged into v3 that any future merge into v4 that this is changed to DateTimeKind.Utc. This test may still work in v4 but it could have an issue from changing the default: DynamoDB RetrieveDateTimeInUtc has been switched to true as the default.
Reminder what an epoch is: number of seconds that have passed since Thursday 1 January 1970 00:00:00 UTC
@@ -0,0 +1,11 @@ | |||
{ | |||
"services": [ | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You also changed Core
(sdk/src/Core/Amazon.Util/AWSSDKUtils.cs
) but didn't include it here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for catching it. Fixed. Used updateMinimum
as false
since only AWSSDK.DynamoDBv2
uses it.
9ea348c
to
5eba8fd
Compare
5eba8fd
to
037f1b2
Compare
values outside of 2038 will silently be converted to its original value, in this case, a datetime.
This prevents big API changes
…rrect DateTime epoch serialization issue.
037f1b2
to
7bce707
Compare
7bce707
to
c5b619b
Compare
/// <returns>Converted DateTime structure</returns> | ||
public static DateTime ConvertFromUnixLongEpochSeconds(long seconds) | ||
{ | ||
return new DateTime(seconds * 10000000L + EPOCH_START.Ticks, DateTimeKind.Utc).ToLocalTime(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMPORTANT: We need to ensure if this PR is merged into v3 that any future merge into v4 removes the .ToLocalTime()
.
var longEpochTime = new DateTime(2039, 2, 5, 17, 49, 55, DateTimeKind.Local); // DateTime.Now returns Local kind. | ||
var longEpochTimeBefore1970 = new DateTime(1969, 12, 30, 23, 59, 59, DateTimeKind.Local); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMPORTANT: We need to ensure if this PR is merged into v3 that any future merge into v4 that this is changed to DateTimeKind.Utc. This test may still work in v4 but it could have an issue from changing the default: DynamoDB RetrieveDateTimeInUtc has been switched to true as the default.
Reminder what an epoch is: number of seconds that have passed since Thursday 1 January 1970 00:00:00 UTC
@@ -346,6 +354,7 @@ public void TestContext_CustomDateTimeConverter(bool retrieveDateTimeInUtc) | |||
Context.ConverterCache.Add(typeof(DateTime), new DateTimeUtcConverter()); | |||
|
|||
var currTime = DateTime.Now; | |||
var longEpochTime = new DateTime(2039, 2, 5, 17, 49, 55, DateTimeKind.Local); // DateTime.Now returns Local kind. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMPORTANT: We need to ensure if this PR is merged into v3 that any future merge into v4 that this is changed to DateTimeKind.Utc. This test may still work in v4 but it could have an issue from changing the default: DynamoDB RetrieveDateTimeInUtc has been switched to true as the default.
Reminder what an epoch is: number of seconds that have passed since Thursday 1 January 1970 00:00:00 UTC
var longEpochTime = new DateTime(2039, 7, 23, 2, 3, 4, DateTimeKind.Local); | ||
var longEpochTimeBefore1970 = new DateTime(1955, 12, 30, 23, 59, 59, DateTimeKind.Local); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMPORTANT: We need to ensure if this PR is merged into v3 that any future merge into v4 that this is changed to DateTimeKind.Utc. This test may still work in v4 but it could have an issue from changing the default: DynamoDB RetrieveDateTimeInUtc has been switched to true as the default.
Reminder what an epoch is: number of seconds that have passed since Thursday 1 January 1970 00:00:00 UTC
@@ -25,6 +25,8 @@ public partial class DynamoDBTests : TestBase<AmazonDynamoDBClient> | |||
private static readonly DateTime EpochDate = DateTime.Now.AddDays(7); | |||
private static readonly TimeSpan Epsilon = TimeSpan.FromSeconds(1); | |||
private static readonly int EpochSeconds = AWSSDKUtils.ConvertToUnixEpochSeconds(EpochDate); | |||
private static readonly DateTime LongEpochDate = new DateTime(2039, 1, 1, 2, 13, 23, DateTimeKind.Local); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMPORTANT: We need to ensure if this PR is merged into v3 that any future merge into v4 that this is changed to DateTimeKind.Utc. This test may still work in v4 but it could have an issue from changing the default: DynamoDB RetrieveDateTimeInUtc has been switched to true as the default.
Reminder what an epoch is: number of seconds that have passed since Thursday 1 January 1970 00:00:00 UTC
var longEpochTime = new DateTime(2039, 7, 23, 2, 3, 4, DateTimeKind.Local); | ||
var longEpochTimeBefore1970 = new DateTime(1955, 12, 30, 23, 59, 59, DateTimeKind.Local); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMPORTANT: We need to ensure if this PR is merged into v3 that any future merge into v4 that this is changed to DateTimeKind.Utc. This test may still work in v4 but it could have an issue from changing the default: DynamoDB RetrieveDateTimeInUtc has been switched to true as the default.
Reminder what an epoch is: number of seconds that have passed since Thursday 1 January 1970 00:00:00 UTC
Description
Taking over the PR #3442 to get it across finish line. @sander1095 Thanks for the initial PR.
NOTE (for reviewer):
AWSSSDKUtils.ConvertFromUnixEpochSeconds(long)
to convert the stored epoch seconds string back toDateTime
. Please review for it's correctness.IMPORTANT: Per @boblodgett, there were some UTC DateTime fix implemented in V4. So we need to make sure we don't overwrite these when merging to
v4-development
branch.Motivation and Context
Testing
DRY_RUN-3c72ed89-be29-4254-9bd1-299cbc34e081
completed successfully.DRY_RUN-dff1146e-110b-45ac-97ed-855f4250280f
completed successfully.Screenshots (if appropriate)
Types of changes
Checklist
License