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() };  


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;
    for (int i = 0; i < 32; i=i+4)

        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;
                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();
    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:

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

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.


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


Now convert the values into Binary


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
Inserting the bit in red at the end shifts all bits to the left

Each Binary value is converted back into Decimal:

BitsValue DescriptionBitsValue Description

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.

I have now also got this working in as well to read the date and time.

Private Declare Function DosDateTimeToVariantTime Lib "oleaut32.dll" (ByVal wDosDate As Integer, ByVal wDosTime As Integer, ByRef pvTime As Date) As Long

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
End Sub

Public Function go()

    Dim hello As String

    Dim DOSDATEANDTIME as String = "86BD3254"

    hello = vbDosDate2Date("&H" + DOSDATEANDTIME)

End function

' Originally taken from here:

Public Function vbDosDate2Date(ByRef lngDate As Long) As String
    Dim strDOSTime As String
    Dim strDOSDate As String
    Dim strDate As String
    Dim dateDest As Date

    strDate = Hex$(lngDate)
    strDOSDate = Right(strDate, 4)
    strDOSTime = Left(strDate, 4)

    ' I needed these lines, which swap the HEX values around. You may not.
    strDOSDate = Mid(strDOSDate, 3, 4) & Mid(strDOSDate, 1, 2)
    strDOSTime = Mid(strDOSTime, 3, 4) & Mid(strDOSTime, 1, 2)

    Call DosDateTimeToVariantTime(CInt("&H" & strDOSDate), CInt("&H" & strDOSTime), dateDest)

    vbDosDate2Date = CStr(dateDest)
End Function

Leave a Reply

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

You are commenting using your 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

Blog at

Up ↑

%d bloggers like this: