Converting DOS Date/Time in C# stored as HEX

Here’s some crazy programming that will do your head in, for it’s storage efficiency.

public void Main
{
    DateTime now = DateTime.Now;
    int yearnowfix = (now.Year + 20);

    string[] theArray = { yearnowfix.ToString().Substring(2),  now.Month.ToString(), now.Day.ToString(), now.Hour.ToString(), now.Minute.ToString(), now.Second.ToString() };  

    Console.WriteLine(PlayDateConversion_ToHex(theArray)); 
}


public string PlayDateConversion_ToHex(String[] HumanDate)
{
    String DateHEX = "";

    // Convert the array to Binary, to then convert to Hex.
    Console.WriteLine("This is the Binary:");

    Console.WriteLine(ToBinary(Convert.ToInt32(HumanDate[0]), 7)); // Year + 20 (2 digits only)
    Console.WriteLine(ToBinary(Convert.ToInt32(HumanDate[1]), 4)); // Month
    Console.WriteLine(ToBinary(Convert.ToInt32(HumanDate[2]), 5)); // Day

    String DateBin = ToBinary(Convert.ToInt32(HumanDate[0]), 7) + ToBinary(Convert.ToInt32(HumanDate[1]), 4) + ToBinary(Convert.ToInt32(HumanDate[2]), 5);

    Console.WriteLine("DateBin:" + DateBin); 

    Console.WriteLine(ToBinary(Convert.ToInt32(HumanDate[3]), 5)); // Hour
    Console.WriteLine(ToBinary(Convert.ToInt32(HumanDate[4]), 6)); // Minute
    Console.WriteLine(ToBinary(Convert.ToInt32(HumanDate[5]), 5)); // Second

    String TimeBin = ToBinary(Convert.ToInt32(HumanDate[3]), 5) + ToBinary(Convert.ToInt32(HumanDate[4]), 6) + ToBinary(Convert.ToInt32(HumanDate[5]), 5);

    Console.WriteLine("TimeBin:" + TimeBin);

    String HexValue = "";
    String MasterHexValue = "";
    String MixedTimeDateBin = DateBin + TimeBin;
    Console.WriteLine(MixedTimeDateBin);
    for (int i = 0; i < 32; i=i+4)
    {

        Console.WriteLine(i); 
        Console.WriteLine(MixedTimeDateBin.Substring(i, 4)); 
        switch (MixedTimeDateBin.Substring(i, 4))
        {
            case "0000": HexValue = "0"; break;
            case "0001": HexValue = "1"; break;
            case "0010": HexValue = "2"; break;
            case "0011": HexValue = "3"; break;
            case "0100": HexValue = "4"; break;
            case "0101": HexValue = "5"; break;
            case "0110": HexValue = "6"; break;
            case "0111": HexValue = "7"; break;
            case "1000": HexValue = "8"; break;
            case "1001": HexValue = "9"; break;
            case "1010": HexValue = "A"; break;
            case "1011": HexValue = "B"; break;
            case "1100": HexValue = "C"; break;
            case "1101": HexValue = "D"; break;
            case "1110": HexValue = "E"; break;
            case "1111": HexValue = "F"; break;
            default:
                return "Invalid number";
        } 
        MasterHexValue = MasterHexValue + HexValue;
    }

    Console.WriteLine("HexDate: " + MasterHexValue); 

    String MasterHexValueArrange = "";
    MasterHexValueArrange = MasterHexValue.Substring(6, 2) + MasterHexValue.Substring(4, 2) + MasterHexValue.Substring(2, 2) + MasterHexValue.Substring(0, 2);

    Console.WriteLine("Remixed HexDate: " + MasterHexValueArrange);  
    return MasterHexValueArrange;
}
 
public static string ToBinary(Int64 Decimal, int leadingzeros)
{
    // Declare a few variables we're going to need
    Int64 BinaryHolder;
    char[] BinaryArray;
    string BinaryResult = "";

    while (Decimal > 0)
    {
        BinaryHolder = Decimal % 2;
        BinaryResult += BinaryHolder;
        Decimal = Decimal / 2;
    }

    BinaryArray = BinaryResult.ToCharArray();
    Array.Reverse(BinaryArray);
    BinaryResult = new string(BinaryArray);

    return BinaryResult.ToString().PadLeft(leadingzeros, '0');
}


This proved to be a bit of fun, but interesting all the same. Basically, it allows you to store the date and time of YYYYMMDDHHMMSS in to 4 Characters, stored as Hex values.

I did the maths for this to happen on the above code. This storage method of the date and time is probably one of the most complicated ways of storing the date and time due to the conversion from Decimal to Binary and then eventually to HEX Base-15, however it’s also the most character space saving method I’ve seen as well. I didn’t write this method, but some boffins did. I just made it so you can get the “now” time at the end of the routine.

This very efficient date storage is also used in StationPlaylist APE XLastPlayed metadata tag.

I’ve left all the description and workings out in the code so others can learn. No doubt it can be cleaned up further.

You can test these things using HxD hex editor and hoving over 2 characters which will give you a valid date or time under it’s Data Inspector tool.

The original maths came from here: https://doubleblak.com/blogPosts.php?id=7#dosTime

The following has just been copied from the page.

DOS FileTime

The DOS TimeStamp can be viewed in either Date/Time or Time/Date order. The method for calculating both is the same however.

Example : The current time in DOS (Date/Time)TimeStamp is 9651F706.
Example : The current time in DOS (Time/Date) TimeStamp is F7069651.

Epoch DateN/A
UnitN/A
ExpressionHex
TimeZoneLocal

Start by splitting the byte up into two nibbles. These represent the Date and Time (Or Time and Date).

In this example I will use the Date/Time format.

3050C57B
DateTime
3050C57B

Next, the nibbles need to be switched around to read in the opposite endian.

3050C57B
DateTime
3050C57B
50307BC5

Now convert the values into Binary

CA7B3050
50307BCA
DateTime
50307BC5
01010000001100000111101111000101

The Binary is then broken up into sections.

Note that only 5 bits are available to record the seconds. That would usually mean a maximum number of seconds of 31 (16 + 8 + 4 + 2 + 1). We obviously need more than that but there aren’t enough bits available. The decision was made that accuracy to 2 seconds would be enough. That meant that you can assume that the least significant bit is always 0. So by adding a 0 to the right side, you end up with 6 bits.

Bit 1Bit 2Bit 3Bit 4Bit 5Bit 6Max Value
 1111131
11111062
Inserting the bit in red at the end shifts all bits to the left
CA7B3050
50307BCA
DateTime
50307BC5
01010000001100000111101111000101
BitsValueDescriptionBitsValueDescription
0-60101000Year0-401111Hour
7-100001Month5-10011110Minute
11-1510000Day11-15001010Second

Each Binary value is converted back into Decimal:

CA7B3050
50307BCA
DateTime
50307BCA
01010000001100000111101111001010
BitsValue DescriptionBitsValue Description
0-6010100040Year0-40111115Hour
7-1000011Month5-1001111030Minute
11-151000016Day11-1500101010Second

So you have the 16th January 40AD at 15:30:10… Oh wait… You may have realised that the year is a little off.

When this timestamp was created, they realized that seconds, minutes, hours, days and months are all finite, with top values of 60, 60, 24, 31 and 12 respectively. Years however presents an issue as they go on (hopefully) almost infinitely.

To combat this, the developers decided that 1980 would be a good place to start and so the YEAR value is added to 1980 in every case. In the example above, the year value is 40. Therefore, 1980 + 40 = 2020.

So the final date is 16th January 2020 at 15:30:10.

Note that these are always LOCAL TIME.

Convert your own DOS Date/Time TimeStamp here :  = 

Convert your own DOS Time/Date TimeStamp here :  = 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a free website or blog at WordPress.com.

Up ↑

%d bloggers like this: