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 Date | N/A |
Unit | N/A |
Expression | Hex |
TimeZone | Local |
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 | |
Date | Time |
3050 | C57B |
Next, the nibbles need to be switched around to read in the opposite endian.
3050C57B | |
Date | Time |
3050 | C57B |
5030 | 7BC5 |
Now convert the values into Binary
CA7B3050 | |
50307BCA | |
Date | Time |
5030 | 7BC5 |
0101000000110000 | 0111101111000101 |
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 1 | Bit 2 | Bit 3 | Bit 4 | Bit 5 | Bit 6 | Max Value |
1 | 1 | 1 | 1 | 1 | 31 | |
1 | 1 | 1 | 1 | 1 | 0 | 62 |
Inserting the bit in red at the end shifts all bits to the left |
CA7B3050 | |||||
50307BCA | |||||
Date | Time | ||||
5030 | 7BC5 | ||||
0101000000110000 | 0111101111000101 | ||||
Bits | Value | Description | Bits | Value | Description |
0-6 | 0101000 | Year | 0-4 | 01111 | Hour |
7-10 | 0001 | Month | 5-10 | 011110 | Minute |
11-15 | 10000 | Day | 11-15 | 001010 | Second |
Each Binary value is converted back into Decimal:
CA7B3050 | |||||||
50307BCA | |||||||
Date | Time | ||||||
5030 | 7BCA | ||||||
0101000000110000 | 0111101111001010 | ||||||
Bits | Value | Description | Bits | Value | Description | ||
0-6 | 0101000 | 40 | Year | 0-4 | 01111 | 15 | Hour |
7-10 | 0001 | 1 | Month | 5-10 | 011110 | 30 | Minute |
11-15 | 10000 | 16 | Day | 11-15 | 001010 | 10 | Second |
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 VB.net 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
go()
End Sub
Public Function go()
Dim hello As String
Dim DOSDATEANDTIME as String = "86BD3254"
hello = vbDosDate2Date("&H" + DOSDATEANDTIME)
End function
' Originally taken from here: https://activevb.de/rubriken/faq/faq0115.html
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