Displaying the build date

Viewed 178031

I currently have an app displaying the build number in its title window. That's well and good except it means nothing to most of the users, who want to know if they have the latest build - they tend to refer to it as "last Thursday's" rather than build 1.0.8.4321.

The plan is to put the build date there instead - So "App built on 21/10/2009" for example.

I'm struggling to find a programmatic way to pull the build date out as a text string for use like this.

For the build number, I used:

Assembly.GetExecutingAssembly().GetName().Version.ToString()

after defining how those came up.

I'd like something like that for the compile date (and time, for bonus points).

Pointers here much appreciated (excuse pun if appropriate), or neater solutions...

30 Answers

One approach which I'm amazed no-one has mentioned yet is to use T4 Text Templates for code generation.

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System" #>
<#@ output extension=".g.cs" #>
using System;
namespace Foo.Bar
{
    public static partial class Constants
    {
        public static DateTime CompilationTimestampUtc { get { return new DateTime(<# Write(DateTime.UtcNow.Ticks.ToString()); #>L, DateTimeKind.Utc); } }
    }
}

Pros:

  • Locale-independent
  • Allows a lot more than just the time of compilation

Cons:

Lots of great answers here but I feel like I can add my own because of simplicity, performance (comparing to resource-related solutions) cross platform (works with Net Core too) and avoidance of any 3rd party tool. Just add this msbuild target to the csproj.

<Target Name="Date" BeforeTargets="BeforeBuild">
    <WriteLinesToFile File="$(IntermediateOutputPath)gen.cs" Lines="static partial class Builtin { public static long CompileTime = $([System.DateTime]::UtcNow.Ticks) %3B }" Overwrite="true" />
    <ItemGroup>
        <Compile Include="$(IntermediateOutputPath)gen.cs" />
    </ItemGroup>
</Target>

and now you have Builtin.CompileTime in this project, e.g.:

var compileTime = new DateTime(Builtin.CompileTime, DateTimeKind.Utc);

ReSharper is not gonna like it. You can ignore him or add a partial class to the project too but it works anyway.

UPD: Nowadays ReSharper have an option in a first page of Options: "MSBuild access", "Obtain data from MSBuild after each compilation". This helps with visibility of generated code.

For .NET Core projects, I adapted Postlagerkarte's answer to update the assembly Copyright field with the build date.

Directly Edit csproj

The following can be added directly to the first PropertyGroup in the csproj:

<Copyright>Copyright © $([System.DateTime]::UtcNow.Year) Travis Troyer ($([System.DateTime]::UtcNow.ToString("s")))</Copyright>

Alternative: Visual Studio Project Properties

Or paste the inner expression directly into the Copyright field in the Package section of the project properties in Visual Studio:

Copyright © $([System.DateTime]::UtcNow.Year) Travis Troyer ($([System.DateTime]::UtcNow.ToString("s")))

This can be a little confusing, because Visual Studio will evaluate the expression and display the current value in the window, but it will also update the project file appropriately behind the scenes.

Solution-wide via Directory.Build.props

You can plop the <Copyright> element above into a Directory.Build.props file in your solution root, and have it automatically applied to all projects within the directory, assuming each project does not supply its own Copyright value.

<Project>
 <PropertyGroup>
   <Copyright>Copyright © $([System.DateTime]::UtcNow.Year) Travis Troyer ($([System.DateTime]::UtcNow.ToString("s")))</Copyright>
 </PropertyGroup>
</Project>

Directory.Build.props: Customize your build

Output

The example expression will give you a copyright like this:

Copyright © 2018 Travis Troyer (2018-05-30T14:46:23)

Retrieval

You can view the copyright information from the file properties in Windows, or grab it at runtime:

var version = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location);

Console.WriteLine(version.LegalCopyright);

In 2018 some of the above solutions do not work anymore or do not work with .NET Core.

I use the following approach which is simple and works for my .NET Core 2.0 project.

Add the following to your .csproj inside the PropertyGroup :

    <Today>$([System.DateTime]::Now)</Today>

This defines a PropertyFunction which you can access in your pre build command.

Your pre-build looks like this

echo $(today) > $(ProjectDir)BuildTimeStamp.txt

Set the property of the BuildTimeStamp.txt to Embedded resource.

Now you can read the time stamp like this

public static class BuildTimeStamp
    {
        public static string GetTimestamp()
        {
            var assembly = Assembly.GetEntryAssembly(); 

            var stream = assembly.GetManifestResourceStream("NamespaceGoesHere.BuildTimeStamp.txt");

            using (var reader = new StreamReader(stream))
            {
                return reader.ReadToEnd();
            }
        }
    }

I just do:

File.GetCreationTime(GetType().Assembly.Location)

You can use this project: https://github.com/dwcullop/BuildInfo

It leverages T4 to automate the build date timestamp. There are several versions (different branches) including one that gives you the Git Hash of the currently checked out branch, if you're into that sort of thing.

Disclosure: I wrote the module.

For projects on .NET 5+, it can be done like this. Nice in that there are no files to add or embed, no T4, and no pre-build scripts.

Add a class like this to your project:

namespace SuperDuper
{
    [AttributeUsage(AttributeTargets.Assembly)]
    public class BuildDateTimeAttribute : Attribute
    {
        public string Date { get; set; }
        public BuildDateTimeAttribute(string date)
        {
            Date = date;
        }
    }
}

Update the .csproj of your project to include something like this:

<ItemGroup>
    <AssemblyAttribute Include="SuperDuper.BuildDateTime">
        <_Parameter1>$([System.DateTime]::Now.ToString("s"))</_Parameter1>
    </AssemblyAttribute>
</ItemGroup>

Note that _Parameter1 is a magical name - it means the first (and only) argument to the constructor of our BuildDateTime attribute class.

That's all that is needed to record the build datetime in your assembly.

And then to read the build datetime of your assembly, do something like this:

private static DateTime? getAssemblyBuildDateTime()
{
    var assembly = System.Reflection.Assembly.GetExecutingAssembly();
    var attr = Attribute.GetCustomAttribute(assembly, typeof(BuildDateTimeAttribute)) as BuildDateTimeAttribute;
    if (DateTime.TryParse(attr?.Date, out DateTime dt))
        return dt;
    else
        return null;
}

Note (per Flydog57 in the comments) that if your .csproj has property GenerateAssemblyInfo listed in it and set to false, the build won't generate assembly info and you'll get no BuildDateTime info in your assembly. So either do not mention GenerateAssemblyInfo in your .csproj (this is the default behaviour for a new project, and GenerateAssemblyInfo defaults to true if not specifically set to false), or explicitly set it to true.

A small update on the "New Way" answer from Jhon.

You need to build the path instead of using the CodeBase string when working with ASP.NET/MVC

    var codeBase = assembly.GetName().CodeBase;
    UriBuilder uri = new UriBuilder(codeBase);
    string path = Uri.UnescapeDataString(uri.Path);

A full solution step by step for Visual Studio 2019, like the one I wish I had found when I began years ago.

Add a text resource file

Access the properties of your project: from the solution explorer, select your project, then right-click -> properties, or Alt+Enter. In the Resources tab, choose Files (Ctrl+5). Then Add Resource / Add New Text File. In the popup message, type the name of your resource, for example BuildDate: this will create a new text file BuildDate.txt in your Project/Resources folder, include it as Project file, and register it as a resource, which can then be accessed via Properties.Resources in C#, or My.Resources in VB.

Automatically update the resource file each time you build

Now you can tell Visual Studio to write a date into this file, each time it builds or rebuilds the project. For this, go to the Compile tab of the Project Properties, choose Build Events, and copy/paste the following into the "Pre-Build event command line" textbox:

powershell -Command "((Get-Date).ToUniversalTime()).ToString(\"s\") | Out-File '$(ProjectDir)Resources\BuildDate.txt'"

This line will locate BuildDate.txt and write today/NowUtc's date and time under the ISO8601 format, such as 2021-09-07T16:08:35

Obtain the build date at run-time by reading the file

You can then retrieve this date from your code at run-time, via the following helper (C#):

DateTime CurrentBuildDate = DateTime.Parse(Properties.Resources.BuildDate, null, System.Globalization.DateTimeStyles.RoundtripKind);

Credits

I just added pre-build event command:

powershell -Command Get-Date -Format 'yyyy-MM-ddTHH:mm:sszzz' > Resources\BuildDateTime.txt

in the project properties to generate a resource file that is then easy to read from the code.

I had difficulties with the suggested solutions with my project, a .Net Core 2.1 web application. I combined various suggestions from above and simplified, and also converted the date to my required format.

The echo command:

echo Build %DATE:~-4%/%DATE:~-10,2%/%DATE:~-7,2% %time% > "$(ProjectDir)\BuildDate.txt"

The code:

Logger.Info(File.ReadAllText(@"./BuildDate.txt").Trim());

It seems to work. The output:

2021-03-25 18:41:40,877 [1] INFO Config - Build 2021/03/25 18:41:37.58

Nothing very original, I just combined suggestions from here and other related questions, and simplified.

For .NET 5 I've used this method successfully. (Found here).

Add this to the .csproj file:

<SourceRevisionId>build$([System.DateTime]::UtcNow.ToString("yyyyMMddHHmmss"))</SourceRevisionId>

Method for getting build date:

private static DateTime GetBuildDate(Assembly assembly)
{
    const string BuildVersionMetadataPrefix = "+build";

    var attribute = assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
    if (attribute?.InformationalVersion != null)
    {
        var value = attribute.InformationalVersion;
        var index = value.IndexOf(BuildVersionMetadataPrefix);
        if (index > 0)
        {
            value = value.Substring(index + BuildVersionMetadataPrefix.Length);
            if (DateTime.TryParseExact(value, "yyyyMMddHHmmss", CultureInfo.InvariantCulture, DateTimeStyles.None, out var result))
            {
                return result;
            }
        }
    }

    return default;
}

Usage:

 var buildTime = GetBuildDate(Assembly.GetExecutingAssembly());
 buildTime = buildTime.ToLocalTime();

GetLastWriteTime isn't changed if you copy the assembly to another location.

public static class AssemblyExtensions
{
    public static DateTime GetLinkerTime(this Assembly assembly)
    {
        return File.GetLastWriteTime(assembly.Location).ToLocalTime();
    }
}

it could be

Assembly execAssembly = Assembly.GetExecutingAssembly();
var creationTime = new FileInfo(execAssembly.Location).CreationTime;
// "2019-09-08T14:29:12.2286642-04:00"
Related