Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Problem
If a user in the eastern hemisphere tries to install a version of a mod that was released in the past X hours, where X is the number of timezones east of Greenwich (so, 3 hours for UTC+3), it fails with this error:
Cause
CKAN thinks the freshly downloaded file is older than the release date, which ordinarily means it needs to re-download the file because it was replaced on the server:
CkanModule.release_date
is being affected by two serious design defects in different components:Newtonsoft.Json
(de-)serializes a UTC timestamp, such asCkanModule.release_date
, it does not preserve the time zone as-is or convert it to UTC, as any reasonable programmer would expect. Instead, it converts to local time!! If you print it, it shows the correct time zone offset, so in theory it has all the info needed to recreate the original timestamp, except...DateTime
has two modes, local and UTC (it is unable to represent a timestamp in any of the ~22 other time zones), and<
and>
comparison operators. When it compares a local timestamp to a UTC timestamp, it does not convert them to the same time zone, even though it has all the information it would need to do so!! Instead it just compares the raw numbers as if they were already in the same time zone. This means it gives wrong answers unless the calling code uses a mechanism like.ToUniversalTime()
carefully and strategically to ensure consistency.Together, these two very surprising choices in the runtime and libraries break this simple line of code for people east of Greenwich:
CKAN/Core/Net/NetFileCache.cs
Lines 221 to 222 in 9addf80
remoteTimestamp
is a local timestamp (despite Netkan generating and saving it in UTC originally), so it will be treated as greater than an actually-equivalent-or-slightly-less UTC timestamp returned byFile.GetLastWriteTime(file).ToUniversalTime()
if the local hour is greater than the UTC hour, i.e. if the time zone offset is negative, i.e. if you're in the eastern hemisphere.The western hemisphere has the opposite, far less obvious problem. There, remote timestamps are treated as older than they really are by your distance from Greenwich, so if a mod author replaces a download, a user re-installing that mod will not get the latest changes if they first downloaded it just a few hours before the replacement.
Changes
Now we set
JsonSerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc
to tell our JSON deserializers to convert timestamps to UTC so they can be used safely and sanely. The one inCkanModule.FromJson
fixes loading freshly downloaded repo metadata, and the one inRepositoryData.FromJson
handles loading cached repo metadata. The others are included just in case.Fixes #3972.