Determining Build Date the hard way

One of the key diagnostic data points for any .NET assembly is “when was it built?” Until recently, I thought there were only two ways to suss this out:

  1. Check the filesystem date and time
  2. Derive the build date from the assembly version

The filesystem method has obvious limitations:

Function AssemblyLastWriteTime(ByVal a As Reflection.Assembly) As DateTime
Try
Return File.GetLastWriteTime(a.Location)
Catch ex As Exception
Return DateTime.MaxValue
End Try
End Function

The version method, however, works quite well – as long as developers don’t deviate too far from the default .NET version string of <Assembly: AssemblyVersion("1.0.*")>

When specifying a version, you have to at least specify major. If you specify major and minor, you can specify an asterisk (*) for build. This will cause build to be equal to the number of days since January 1, 2000 local time, and for revision to be equal to the number of seconds since midnight local time, divided by 2.

If you specify major, minor, and build, you can specify an asterisk for revision. This will cause revision to be equal to the number of seconds since midnight local time, divided by 2.
Function AssemblyBuildDate(ByVal a As Reflection.Assembly, _
Optional ByVal forceFileDate As Boolean = False) As DateTime
Dim v As System.Version = a.GetName.Version
Dim dt As DateTime
If forceFileDate OrElse (v.Build < 730 Or v.Revision = 0) Then
dt = AssemblyLastWriteTime(a)
Else
dt = New DateTime(2000, 1, 1, 0, 0, 0). _
AddDays(v.Build). _
AddSeconds(v.Revision * 2)
If TimeZone.IsDaylightSavingTime(dt, _
TimeZone.CurrentTimeZone.GetDaylightChanges(dt.Year)) Then
dt = dt.AddHours(1)
End If
'-- sanity check
If dt > DateTime.Now Or dt < New DateTime(2000, 1, 1, 0, 0, 0) Then
dt = AssemblyLastWriteTime(a)
End If
End If
Return dt
End Function

Be careful when relying on version to predict build date in Visual Studio .NET. For some reason, the IDE does not update the build number every time you build a solution. Visual Studio only increments the build and revision number when the solution is closed and reopened. If you build fifty times throughout the day in the same solution, every single one of your builds will have the same version. Close and reopen that solution, though, and you’ll get a new version immediately. Go figure.

Luckily, we don’t have to settle for those two options. There’s a third way to calculate build date that’s much more reliable. Dustin Aleksiuk recently posted a clever blog entry describing how to retrieve the embedded linker timestamp from the IMAGE_FILE_HEADER section of the Portable Executable header:

Function RetrieveLinkerTimestamp(ByVal filePath As String) As DateTime
Const PeHeaderOffset As Integer = 60
Const LinkerTimestampOffset As Integer = 8
Dim b(2047) As Byte
Dim s As Stream
Try
s = New FileStream(filePath, FileMode.Open, FileAccess.Read)
s.Read(b, 0, 2048)
Finally
If Not s Is Nothing Then s.Close()
End Try
Dim i As Integer = BitConverter.ToInt32(b, PeHeaderOffset)
Dim SecondsSince1970 As Integer = BitConverter.ToInt32(b, i + LinkerTimestampOffset)
Dim dt As New DateTime(1970, 1, 1, 0, 0, 0)
dt = dt.AddSeconds(SecondsSince1970)
dt = dt.AddHours(TimeZone.CurrentTimeZone.GetUtcOffset(dt).Hours)
Return dt
End Function

When I ran Dustin’s code for the first time, I wondered why the dates and minutes were correct, but the hours were consistently off by four. Even I can figure out GMT/UTC issues when they practically slap me in the face. I emailed Dustin to ask him what he thought, and as it turns out, Dustin lives in GMT that’s the ultimate “it runs on my machine!” Sure does make those pesky mental IIS logfile date conversions easier, too... ;)

Related posts

Why Ruby?

I've been a Microsoft developer for decades now. I weaned myself on various flavors of home computer Microsoft Basic, and I got my first paid programming gigs in Microsoft FoxPro, Microsoft Access, and Microsoft Visual Basic. I have seen the future of programming, my friends, and it is

By Jeff Atwood ·
Comments

Donating $5,000 to .NET Open Source

Way back in June of last year, I promised to donate a portion of my advertising revenue back to the community: I will be donating a significant percentage of my ad revenue back to the programming community. The programming community is the reason I started this blog in the first

By Jeff Atwood ·
Comments

Do Not Buy This Book

A few friends and I just wrote a book together: The ASP.NET 2.0 Anthology: 101 Essential Tips, Tricks & Hacks. I met K. Scott Allen, Jon Galloway, and Phil Haack through their excellent blogs. That online friendship carried over into real life. We always thought it'd

By Jeff Atwood ·
Comments

Defining Open Source

As I mentioned two weeks ago, my plan is to contribute $10,000 to the .NET open source ecosystem. $5,000 from me, and a matching donation of $5,000 from Microsoft. There's only two ground rules so far: 1. The project must be written in .NET managed

By Jeff Atwood ·
Comments

Recent Posts

Stay Gold, America

Stay Gold, America

We are at an unprecedented point in American history, and I'm concerned we may lose sight of the American Dream.

By Jeff Atwood ·
Comments
The Great Filter Comes For Us All

The Great Filter Comes For Us All

With a 13 billion year head start on evolution, why haven’t any other forms of life in the universe contacted us by now? (Arrival is a fantastic movie. Watch it, but don’t stop there – read the Story of Your Life novella it was based on for so much

By Jeff Atwood ·
Comments
I Fight For The Users

I Fight For The Users

If you haven’t been able to keep up with my blistering pace of one blog post per year, I don’t blame you. There’s a lot going on right now. It’s a busy time. But let’s pause and take a moment to celebrate that Elon Musk

By Jeff Atwood ·
Comments
The 2030 Self-Driving Car Bet

The 2030 Self-Driving Car Bet

It’s my honor to announce that John Carmack and I have initiated a friendly bet of $10,000* to the 501(c)(3) charity of the winner’s choice: By January 1st, 2030, completely autonomous self-driving cars meeting SAE J3016 level 5 will be commercially available for passenger use

By Jeff Atwood ·
Comments