我目前有一个应用程序显示构建号在其标题窗口。这很好,除了它对大多数用户没有任何意义,他们想知道他们是否有最新的版本-他们倾向于将其称为“上周四的”而不是1.0.8.4321版本。

计划是把构建日期放在那里,例如“App构建于21/10/2009”。

我正在努力寻找一种程序化的方法,将构建日期作为文本字符串提取出来,以便像这样使用。

对于版本号,我使用:

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

在定义了这些是怎么来的之后。

我希望编译日期(和时间,为了加分)也像这样。

非常感谢这里的指示(如果合适的话,借口双关语),或者更整洁的解决方案……


当前回答

在2018年,上面的一些解决方案不再工作,或者不能与。net Core一起工作。

我使用下面的方法,它很简单,适用于我的。net Core 2.0项目。

将以下内容添加到PropertyGroup中的.csproj中:

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

这定义了一个PropertyFunction,您可以在预构建命令中访问它。

您的预构建看起来像这样

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

将BuildTimeStamp.txt的属性设置为嵌入式资源。

现在你可以像这样读时间戳

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

其他回答

我不确定,但也许Build Incrementer会有所帮助。

Jeff Atwood在《艰难地确定构建日期》中谈到了这个问题。

最可靠的方法是从可执行文件中嵌入的PE头中检索链接器时间戳——一些c#代码(由Joe Spivey编写)来自Jeff文章的注释:

public static DateTime GetLinkerTime(this Assembly assembly, TimeZoneInfo target = null)
{
    var filePath = assembly.Location;
    const int c_PeHeaderOffset = 60;
    const int c_LinkerTimestampOffset = 8;

    var buffer = new byte[2048];

    using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
        stream.Read(buffer, 0, 2048);

    var offset = BitConverter.ToInt32(buffer, c_PeHeaderOffset);
    var secondsSince1970 = BitConverter.ToInt32(buffer, offset + c_LinkerTimestampOffset);
    var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

    var linkTimeUtc = epoch.AddSeconds(secondsSince1970);

    var tz = target ?? TimeZoneInfo.Local;
    var localTime = TimeZoneInfo.ConvertTimeFromUtc(linkTimeUtc, tz);

    return localTime;
}

使用的例子:

var linkTimeLocal = Assembly.GetExecutingAssembly().GetLinkerTime();

注意:这个方法适用于。net Core 1.0,但在。net Core 1.1之后就不再适用了——它给出的年份是1900-2020年之间的随机值。

如果这是一个windows应用程序,你可以只使用应用程序的可执行路径: 新System.IO.FileInfo (Application.ExecutablePath) .LastWriteTime.ToString(“yyyy.MM.dd”)

有一种方法是使用T4文本模板来生成代码,这让我很惊讶。

<#@ 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); } }
    }
}

优点:

Locale-independent 允许的不仅仅是编译时间

缺点:

仅适用于您控制源代码的库 需要配置您的项目(以及构建服务器,如果它无法接收的话),以便在预构建步骤中执行模板。(参见T4不含VS)。

我需要一个通用的解决方案,可以在任何平台(iOS、Android和Windows)上与NETStandard项目一起工作。为了实现这一点,我决定通过PowerShell脚本自动生成CS文件。下面是PowerShell脚本:

param($outputFile="BuildDate.cs")

$buildDate = Get-Date -date (Get-Date).ToUniversalTime() -Format o
$class = 
"using System;
using System.Globalization;

namespace MyNamespace
{
    public static class BuildDate
    {
        public const string BuildDateString = `"$buildDate`";
        public static readonly DateTime BuildDateUtc = DateTime.Parse(BuildDateString, null, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
    }
}"

Set-Content -Path $outputFile -Value $class

将PowerScript文件保存为GenBuildDate。Ps1,并添加到你的项目。最后,添加以下行到您的预构建事件:

powershell -File $(ProjectDir)GenBuildDate.ps1 -outputFile $(ProjectDir)BuildDate.cs

确保项目中包含BuildDate.cs。在任何操作系统上都像冠军一样!