Skip to content
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

Add support for Unix Epoch #82

Merged
merged 1 commit into from
Jun 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion source/nanoFramework.CoreLibrary/System/AssemblyInfo2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
[assembly: AssemblyProduct("nanoFramework mscorlib")]
[assembly: AssemblyCopyright("Copyright © nanoFramework Contributors 2017")]

[assembly: AssemblyNativeVersion("1.2.0.0")]
[assembly: AssemblyNativeVersion("1.2.1.0")]
67 changes: 67 additions & 0 deletions source/nanoFramework.CoreLibrary/System/DateTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ public struct DateTime
private const int MillisPerHour = MillisPerMinute * 60;
private const int MillisPerDay = MillisPerHour * 24;

// Unix Epoch constants
private const long UnixEpochTicks = (TicksPerDay * 719162) - _ticksAtOrigin; // 621355968000000000
private const long UnixEpochSeconds = UnixEpochTicks / TicksPerSecond; // 62135596800

private const long MinTicks = 0;
// ticks value corresponding to 3000/12/31:23:59:59.999
private const long MaxTicks = 946708127999999999 - _ticksAtOrigin;
Expand All @@ -116,6 +120,14 @@ public struct DateTime
/// </remarks>
public static readonly DateTime MaxValue = new DateTime(MaxTicks + _ticksAtOrigin);

/// <summary>
/// Represents the Unix Epoch value. This field is read-only.
/// </summary>
/// <remarks>The value of this constant is equivalent to the <see cref="DateTime"/> corresponding to 1970-01-01T00:00:00Z (January 1, 1970, at 12:00 AM UTC).
/// This value is specific to nanoFramework.
/// </remarks>
public static readonly DateTime UnixEpoch = new DateTime(UnixEpochTicks + _ticksAtOrigin, DateTimeKind.Utc);

// The data is stored as an unsigned 64-bit integer
// Bits 01-62: The value of 100-nanosecond ticks where 0 represents 1601/01/01:00:00:00.000, up until the value
// 3000/12/31:23:59:59.999
Expand Down Expand Up @@ -701,6 +713,61 @@ public String ToString(String format)
return Compare(t1, t2) >= 0;
}

/// <summary>
/// Converts a Unix time expressed as the number of seconds that have elapsed since 1970-01-01T00:00:00Z to a <see cref="DateTime"/> value.
/// </summary>
/// <param name="seconds">A Unix time, expressed as the number of seconds that have elapsed since 1970-01-01T00:00:00Z (January 1, 1970, at 12:00 AM UTC). For Unix times before this date, its value is negative.</param>
/// <returns>A date and time value that represents the same moment in time as the Unix time.</returns>
/// <remarks>
/// This method is exclusive of nanoFramework.
/// </remarks>
public static DateTime FromUnixTimeSeconds(long seconds)
{
const long MinSeconds = (MinTicks / TicksPerSecond) - UnixEpochSeconds;
const long MaxSeconds = (MaxTicks / TicksPerSecond) - UnixEpochSeconds;

if (seconds < MinSeconds || seconds > MaxSeconds)
{
#pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one
throw new ArgumentOutOfRangeException();
#pragma warning restore S3928 // Parameter names used into ArgumentException constructors should match an existing one
}

long ticks = (seconds * TicksPerSecond) + UnixEpochTicks;
return new DateTime(ticks + _ticksAtOrigin);
}

/// <summary>
/// Returns the number of seconds that have elapsed since 1970-01-01T00:00:00Z.
/// </summary>
/// <returns>The number of seconds that have elapsed since 1970-01-01T00:00:00Z.</returns>
/// <remarks>
/// Unix time represents the number of seconds that have elapsed since 1970-01-01T00:00:00Z (January 1, 1970, at 12:00 AM UTC). It does not take leap seconds into account.
///
/// This method is exclusive of nanoFramework.
/// </remarks>
public long ToUnixTimeSeconds()
{
// Truncate sub-second precision before offsetting by the Unix Epoch to avoid
// the last digit being off by one for dates that result in negative Unix times.
//
// For example, consider the DateTime 12/31/1969 12:59:59.001 +0
// ticks = 621355967990010000
// ticksFromEpoch = ticks - UnixEpochTicks = -9990000
// secondsFromEpoch = ticksFromEpoch / TicksPerSecond = 0
//
// Notice that secondsFromEpoch is rounded *up* by the truncation induced by integer division,
// whereas we actually always want to round *down* when converting to Unix time. This happens
// automatically for positive Unix time values. Now the example becomes:
// seconds = ticks / TicksPerSecond = 62135596799
// secondsFromEpoch = seconds - UnixEpochSeconds = -1
//
// In other words, we want to consistently round toward the time 1/1/0001 00:00:00,
// rather than toward the Unix Epoch (1/1/1970 00:00:00).
long seconds = (long)(_ticks / TicksPerSecond);
return seconds - UnixEpochSeconds;
}

/// <summary>
/// Returns the hash code for this instance.
/// </summary>
Expand Down