Generating different types of timestamps from quite a long way away


Today's Little Program does the reverse of what we had last time. It takes a point in time and then generates timestamps in various formats.

using System;

class Program
{
 static void TryFormat(string format, Func<long> func)
 {
  try {
   long l = func();
   if ((ulong)l > 0x00000000FFFFFFFF) {
       Console.WriteLine("{0} 0x{1:X16}", format, l);
   } else {
       Console.WriteLine("{0} 0x{1:X08}", format, l);
   }
  } catch (ArgumentException) {
   Console.WriteLine("{0} - invalid", format);
  }
 }

Like last time, the Try­Format method executes the passed-in function inside a try/catch block. If the function executes successfully, then we print the result. There is a tiny bit of cleverness where we choose the output format depending on the number of bits in the result.

 static long DosDateTimeFromDateTime(DateTime value)
 {
  int result = ((value.Year - 1980) << 25) |
               (value.Month << 21) |
               (value.Day << 16) |
               (value.Hour << 11) |
               (value.Minute << 5) |
               (value.Second >> 1);
  return (uint)result;
 }

The Dos­Date­Time­From­Date­Time converts the Date­Time into a 32-bit date/time stamp in MS-DOS format. This is not quite correct because MS-DOS format date/time stamps are in local time, but we are not converting the incoming Date­Time to local time. It's up to you to understand what's going on.

 public static void Main(string[] args)
 {
  int[] parts = new int[7];
  for (int i = 0; i < 7; i++) {
   parts[i] = args.Length > i ? int.Parse(args[i]) : 0;
  }

  DateTime value = new DateTime(parts[0], parts[1], parts[2],
                                parts[3], parts[4], parts[5],
                                parts[6], DateTimeKind.Utc);

  Console.WriteLine("Timestamp {0} UTC", value);

  TryFormat("Unix time",
    () => value.ToFileTimeUtc() / 10000000 - 11644473600);
  TryFormat("UTC FILETIME",
    () => value.ToFileTimeUtc());
  TryFormat("Binary DateTime",
    () => value.ToBinary());
  TryFormat("MS-DOS Date/Time",
    () => DosDateTimeFromDateTime(value));
  TryFormat("OLE Date/Time",
    () => BitConverter.DoubleToInt64Bits(value.ToOADate()));
 }
}

The parameters on the command line are the year, month, day, hour, minute, second, and millisecond; any omitted parameters are taken as zero. We create a UTC Date­Time from it, and then try to convert that Date­Time into the other formats.

[Raymond is currently away; this message was pre-recorded.]

Comments (7)
  1. anonymouscommenter says:

    Nitpicky but I've only iterate until args.Length and then avoid the unnecessary if in the loop - works since values are initialized to zero by default anyhow.

  2. anonymouscommenter says:

    The DosDateTimeFromDateTime function omits the 1980 year bias.

    [Oops. Fixed. Thanks. -Raymond]
  3. anonymouscommenter says:

    Why a are you passing a function taking no arguments to TryFormat, only to execute it there once?

    What's the advantage over evaluating these in main and just passing the generated result?

  4. anonymouscommenter says:

    @AC: Look closer. func is called from inside the try block in the function.

  5. anonymouscommenter says:

    I personally would do checked { } around the integral math in this code and in the previous article.  Out-of-range values would throw an exception and thus be eliminated from the output.

  6. anonymouscommenter says:

    @Myria: Normally, "Little Programs" posts run with a disclaimer that they do little to no error checking. The idea is that Raymond is trying to demonstrate a point, and some times error checking code can get in the way of that point. Especially with nitpickers and so forth.

  7. anonymouscommenter says:

    @Joe: I'd argue that the error-checking is actually important here: it eliminates invalid time formats from the output, improving its usefulness and not just robustness.  Also, Raymond's original code already has some error-checking, so why not overflow checking as well?

    If Raymond's example programs call, say, CreateWindowExW and don't check for an error, that's definitely in the realm of what you're talking about, since the program will work.  Here, and in the previous article, overflow checking is a semantic improvement.

Comments are closed.

Skip to main content