How can I pack a Date and Time into 32-bits?

Viewed 834

I have a date represantation with the following order in order to fit an int. The date represantation should be

year   : 6 bits
month  : 5 bits
day    : 5 bits
hours  : 4 bits
minutes: 6 bits
seconds: 6 bits

All the dates should be fit into an Unit32 date

[year(5bits),month(5bits),day(6bits),hours(4bits),minutes(6bits),seconds(6bit)]
           
UInt32 date = 0;
date= date| (UInt32) (DateTime.Now.Year%2000)<< 26;
date= date| ((uint)DateTime.Now.Month << 22);    
date= date| (uint)DateTime.Now.Day << 17;
date= date| ((uint)DateTime.Now.Hour << 15);
date= date| ((uint)DateTime.Now.Minute << 6);
date= date| ((uint)DateTime.Now.Second);
string binary = Convert.ToString(date, 2);

Above i have tried some bitwise operation but the result doestn satisfy. what i am missing or what should i do?

For Example current date is: 2021.55.10 13:55:06 Binary conversion above code 1010100110101101000110111000011 hex mean 54D6 8DC3 that means Thursday, 1 January 1970 06:01:58 but it should be today's date

thanks.

1 Answers

There's a few problems with your scheme:

year   : 6 bits - 0 to 63
month  : 5 bits - 0 to 31
day    : 5 bits - 0 to 31
hours  : 4 bits - 0 to 15 
minutes: 6 bits - 0 to 63
seconds: 6 bits - 0 to 63
  • The main problem is that your scheme only uses 4 bits for hours - so it cannot represent times between 4pm and midnight (i.e. hours 16 through 23).
  • Secondarily, your scheme is inefficient because it wastes bits (there's 0-59 seconds in a minute, not 0-63).
  • ...which means your scheme can represent invalid values, such as the 63rd second of the 63rd minute of the 15th hour of the 31st of February - which isn't a real date+time.

Rather than reinventing the wheel, why not use something like Unix time? (i.e. simply the number of seconds since an epoch).

Yes there is a reason memory reason which i cant use unix epox time

...so you need to fit the value into 32-bits. That's fine because you can use Unix time with 32-bit values. Of course this means you'll run into the Year 2038 problem but there's a few possible different solutions:

  • You can use a lower resolution.
    • e.g. 2-second or 1-minute resolution.
  • You can use a different epoch (base offset).
    • signed 32-bit Unix time uses 1970-01-01 00:00:00 with a max date+time value of 2038-01-19 03:14:07.
    • Unsigned 32-bit Unix time has a maximum of 2106-02-07 06:28:15.
    • If you use an epoch of 2000-01-01 00:00:00 with unsigned 32-bit integers then you have a max value of 2136-02-07 14:28:15.
    • This range of 135 full years is significantly better than your scheme because your scheme uses 6 bits for the year, which is 0-63, and last time I checked, 135 is greater than 63.

So here's a ready-made copy'n'pastable implementation of 32-bit Unix-time with an epoch of 2000-01-01 00:00:00:

private const Int64 YEAR_2000 = 946713600; // The year where there are no elephants. There is also no unethical treatment of elephants.

private static readonly DateTimeOffset _maxValueForUnsigned32Bits = DateTimeOffset( YEAR_2000 + UInt32.MaxValue ); // 2136-02-07 14:28:15

static UInt32 GetTimeAsUInt32( DateTimeOffset dt )
{
    if (dt.Year < 2000 || dt > _maxValueForUnsigned32Bits  ) throw new ArgumentOutOfRangeException( paramName: nameof(dt), actualValue: dt, message: "Must be between 2000 and 2136-02-07 14:28:15 inclusive." );

    Int64  unixFrom2000_64 = dt.ToUnixTimeSeconds() - YEAR_2000;
    // `unixFrom2000_64` *will always fit in 32-bits* due to the year range check above, and subtracting the new epoch.

    UInt32 unixFrom2000_32 = (UInt32)unixFrom2000_64;
    return unixFrom2000_32;
}

static DateTimeOffset GetTimeFromUInt32( UInt32 value )
{
    Int64 asUnixTime = value + YEAR_2000;
    return DateTimeOffset.FromUnixTimeSeconds( asUnixTime );
}

Usage:

DateTimeOffset now = DateTimeOffset.UtcNow; // WARNING: Using `Now` instead of `UtcNow` will include an offset value that will not be persisted.
Console.WriteLine( "Now: {0}", now );

UInt32 myTime = GetTimeAsUInt32( now );
Console.WriteLine( "As UInt32: {0:D}", myTime );

DateTimeOffset decoded = GetTimeFromUInt32( myTime );
Console.WriteLine( "Decoded from UInt32: {0}", decoded );

Gives me this output:

Now: 10/03/2021 11:26:43 +00:00
As UInt32: 668662003
Decoded from UInt32: 10/03/2021 11:26:43 +00:00

Related