C# 显示构建日期

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1600962/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 19:15:56  来源:igfitidea点击:

Displaying the build date

c#datetimecompilation

提问by Mark Mayo

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.

我目前有一个应用程序在其标题窗口中显示内部版本号。这很好,但对于大多数想知道他们是否拥有最新版本的用户来说毫无意义——他们倾向于将其称为“上周四的”而不是版本 1.0.8.4321。

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

计划是将构建日期放在那里 - 例如“应用程序构建于 21/10/2009”。

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...

非常感谢这里的指针(如果合适的话请原谅双关语),或者更简洁的解决方案......

采纳答案by mdb

Jeff Atwood had a few things to say about this issue in Determining Build Date the hard way.

Jeff Atwood 在以艰难的方式确定构建日期中对这个问题有一些话要说。

The most reliable method turns out to be retrieving the linker timestamp from the PE headerembedded in the executable file -- some C# code (by Joe Spivey) for that from the comments to Jeff's article:

事实证明,最可靠的方法是从嵌入在可执行文件中的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;
}

Usage example:

用法示例:

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

UPDATE: The method was working for .Net Core 1.0, but stopped working after .Net Core 1.1release(gives random years in 1900-2020 range)

更新:该方法适用于 .Net Core 1.0,但在 .Net Core 1.1发布后停止工作(给出 1900-2020 范围内的随机年份)

回答by Bobby

I'm not sure, but maybe the Build Incrementerhelps.

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

回答by Guy van den Berg

You could launch an extra step in the build process that writes a date stamp to a file which can then be displayed.

您可以在构建过程中启动一个额外的步骤,将日期戳写入一个文件,然后可以显示该文件。

On the projects properties tab look at the build events tab. There is an option to execute a pre or post build command.

在项目属性选项卡上查看构建事件选项卡。有一个选项可以执行预构建或后构建命令。

回答by MikeWyatt

You could use a project post-build event to write a text file to your target directory with the current datetime. You could then read the value at run-time. It's a little hacky, but it should work.

您可以使用项目构建后事件将文本文件写入具有当前日期时间的目标目录。然后您可以在运行时读取该值。这有点hacky,但它应该可以工作。

回答by Murph

The option not discussed here is to insert your own data into AssemblyInfo.cs, the "AssemblyInformationalVersion" field seems appropriate - we have a couple of projects where we were doing something similar as a build step (however I'm not entirely happy with the way that works so don't really want to reproduce what we've got).

此处未讨论的选项是将您自己的数据插入到 AssemblyInfo.cs 中,“AssemblyInformationalVersion”字段似乎很合适 - 我们有几个项目正在做类似于构建步骤的事情(但是我对的工作方式,所以真的不想重现我们所拥有的)。

There's an article on the subject on codeproject: http://www.codeproject.com/KB/dotnet/Customizing_csproj_files.aspx

有一篇关于 codeproject 主题的文章:http: //www.codeproject.com/KB/dotnet/Customizing_csproj_files.aspx

回答by John Leidegren

The way

道路

As pointed out by @c00000fd in the comments. Microsoft is changing this. And while many people don't use the latest version of their compiler I suspect this change makes this approach unquestionably bad. And while it's a fun exercise I would recommend people to simply embed a build date into their binary through any other means necessary if it's important to track the build date of the binary itself.

正如@c00000fd 在评论中指出的那样。微软正在改变这一点。虽然很多人不使用他们的编译器的最新版本,但我怀疑这种变化无疑会使这种方法变得很糟糕。虽然这是一个有趣的练习,但如果跟踪二进制文件本身的构建日期很重要,我会建议人们通过任何其他必要的方式简单地将构建日期嵌入到他们的二进制文件中。

This can be done with some trivial code generation which probably is the first step in your build script already. That, and the fact that ALM/Build/DevOps tools help a lot with this and should be preferred to anything else.

这可以通过一些简单的代码生成来完成,这可能已经是构建脚本的第一步。这一点,以及 ALM/Build/DevOps 工具对此有很大帮助的事实,应该优先于其他任何工具。

I leave the rest of this answer here for historical purposes only.

我将这个答案的其余部分仅用于历史目的。

The new way

新方式

I changed my mind about this, and currently use this trick to get the correct build date.

我改变了主意,目前使用这个技巧来获得正确的构建日期。

#region Gets the build date and time (by reading the COFF header)

// http://msdn.microsoft.com/en-us/library/ms680313

struct _IMAGE_FILE_HEADER
{
    public ushort Machine;
    public ushort NumberOfSections;
    public uint TimeDateStamp;
    public uint PointerToSymbolTable;
    public uint NumberOfSymbols;
    public ushort SizeOfOptionalHeader;
    public ushort Characteristics;
};

static DateTime GetBuildDateTime(Assembly assembly)
{
    var path = assembly.GetName().CodeBase;
    if (File.Exists(path))
    {
        var buffer = new byte[Math.Max(Marshal.SizeOf(typeof(_IMAGE_FILE_HEADER)), 4)];
        using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read))
        {
            fileStream.Position = 0x3C;
            fileStream.Read(buffer, 0, 4);
            fileStream.Position = BitConverter.ToUInt32(buffer, 0); // COFF header offset
            fileStream.Read(buffer, 0, 4); // "PE
[assembly: AssemblyVersion("1.0.*")] // important: use wildcard for build and revision numbers!
var version = Assembly.GetEntryAssembly().GetName().Version;
var buildDateTime = new DateTime(2000, 1, 1).Add(new TimeSpan(
TimeSpan.TicksPerDay * version.Build + // days since 1 January 2000
TimeSpan.TicksPerSecond * 2 * version.Revision)); // seconds since midnight, (multiply by 2 to get original)
" fileStream.Read(buffer, 0, buffer.Length); } var pinnedBuffer = GCHandle.Alloc(buffer, GCHandleType.Pinned); try { var coffHeader = (_IMAGE_FILE_HEADER)Marshal.PtrToStructure(pinnedBuffer.AddrOfPinnedObject(), typeof(_IMAGE_FILE_HEADER)); return TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1) + new TimeSpan(coffHeader.TimeDateStamp * TimeSpan.TicksPerSecond)); } finally { pinnedBuffer.Free(); } } return new DateTime(); } #endregion

The old way

旧的方式

Well, how do you generate build numbers? Visual Studio (or the C# compiler) actually provides automatic build and revision numbers if you change the AssemblyVersion attribute to e.g. 1.0.*

那么,您如何生成内部版本号?如果您将 AssemblyVersion 属性更改为例如,Visual Studio(或 C# 编译器)实际上会提供自动生成和修订号1.0.*

What will happen is that is that the build will 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.

将会发生的是,构建将等于自当地时间 2000 年 1 月 1 日以来的天数,而修订则等于自当地时间午夜以来的秒数除以 2。

see Community Content, Automatic Build and Revision numbers

请参阅社区内容、自动构建和修订号

e.g. AssemblyInfo.cs

例如 AssemblyInfo.cs

string w_file = "MyProgram.exe"; 
string w_directory = Directory.GetCurrentDirectory();

DateTime c3 =  File.GetLastWriteTime(System.IO.Path.Combine(w_directory, w_file));
RTB_info.AppendText("Program created at: " + c3.ToString());

SampleCode.cs

示例代码.cs

echo %date% %time% > "$(ProjectDir)\Resources\BuildDate.txt"

回答by Zalek Bloom

I am just C# newbie so maybe my answer sound silly - I display the build date from the date the executable file was last written to:

我只是 C# 新手,所以也许我的回答听起来很傻 - 我显示了从可执行文件上次写入的日期开始的构建日期:

    public static async Task<DateTimeOffset?> RetrieveLinkerTimestamp(Assembly assembly)
    {
        var pkg = Windows.ApplicationModel.Package.Current;
        if (null == pkg)
        {
            return null;
        }

        var assemblyFile = await pkg.InstalledLocation.GetFileAsync(assembly.ManifestModule.Name);
        if (null == assemblyFile)
        {
            return null;
        }

        using (var stream = await assemblyFile.OpenSequentialReadAsync())
        {
            using (var reader = new DataReader(stream))
            {
                const int PeHeaderOffset = 60;
                const int LinkerTimestampOffset = 8;

                //read first 2048 bytes from the assembly file.
                byte[] b = new byte[2048];
                await reader.LoadAsync((uint)b.Length);
                reader.ReadBytes(b);
                reader.DetachStream();

                //get the pe header offset
                int i = System.BitConverter.ToInt32(b, PeHeaderOffset);

                //read the linker timestamp from the PE header
                int secondsSince1970 = System.BitConverter.ToInt32(b, i + LinkerTimestampOffset);

                var dt = new DateTimeOffset(1970, 1, 1, 0, 0, 0, DateTimeOffset.Now.Offset) + DateTimeOffset.Now.Offset;
                return dt.AddSeconds(secondsSince1970);
            }
        }
    }

I tried to use File.GetCreationTime method but got weird results: the date from the command was 2012-05-29, but the date from the Window Explorer showed 2012-05-23. After searching for this discrepancy I found that the file was probably created on 2012-05-23 (as shown by Windows Explorer), but copied to the current folder on 2012-05-29 (as shown by File.GetCreationTime command) - so to be on the safe side I am using File.GetLastWriteTime command.

我尝试使用 File.GetCreationTime 方法,但得到了奇怪的结果:命令的日期是 2012-05-29,但 Window Explorer 的日期显示为 2012-05-23。在搜索此差异后,我发现该文件可能是在 2012 年 5 月 23 日创建的(如 Windows 资源管理器所示),但在 2012 年 5 月 29 日复制到当前文件夹(如 File.GetCreationTime 命令所示) - 所以为了安全起见,我正在使用 File.GetLastWriteTime 命令。

Zalek

扎莱克

回答by Abdurrahim

Add below to pre-build event command line:

在预构建事件命令行中添加以下内容:

    public static async Task<DateTimeOffset?> RetrieveLinkerTimestampAsync(Assembly assembly)
    {
        const int PeHeaderOffset = 60;
        const int LinkerTimestampOffset = 8;            
        byte[] b = new byte[2048];

        try
        {
            var rs = Application.GetResourceStream(new Uri(assembly.ManifestModule.Name, UriKind.Relative));
            using (var s = rs.Stream)
            {
                var asyncResult = s.BeginRead(b, 0, b.Length, null, null);
                int bytesRead = await Task.Factory.FromAsync<int>(asyncResult, s.EndRead);
            }
        }
        catch (System.IO.IOException)
        {
            return null;
        }

        int i = System.BitConverter.ToInt32(b, PeHeaderOffset);
        int secondsSince1970 = System.BitConverter.ToInt32(b, i + LinkerTimestampOffset);
        var dt = new DateTimeOffset(1970, 1, 1, 0, 0, 0, DateTimeOffset.Now.Offset) + DateTimeOffset.Now.Offset;
        dt = dt.AddSeconds(secondsSince1970);
        return dt;
    }

Add this file as resource, now you have 'BuildDate' string in your resources.

将此文件添加为资源,现在您的资源中有“BuildDate”字符串。

To create resources, see How to create and use resources in .NET.

要创建资源,请参阅如何在 .NET 中创建和使用资源

回答by Matt Dotson

For anyone that needs to get the compile time in Windows 8 / Windows Phone 8:

对于需要在 Windows 8 / Windows Phone 8 中获取编译时间的任何人:

echo %date% %time% > "$(ProjectDir)\Resources\BuildDate.txt"

For anyone that needs to get the compile time in Windows Phone 7:

对于需要在 Windows Phone 7 中获取编译时间的任何人:

string strCompTime = Properties.Resources.BuildDate;

NOTE: In all cases you're running in a sandbox, so you'll only be able to get the compile time of assemblies that you deploy with your app. (i.e. this won't work on anything in the GAC).

注意:在所有情况下,您都在沙箱中运行,因此您只能获得与应用程序一起部署的程序集的编译时间。(即这不适用于 GAC 中的任何内容)。

回答by brewmanz

Add below to pre-build event command line:

在预构建事件命令行中添加以下内容:

##代码##

Add this file as resource, now you have 'BuildDate' string in your resources.

将此文件添加为资源,现在您的资源中有“BuildDate”字符串。

After inserting the file into the Resource (as public text file), I accessed it via

将文件插入资源(作为公共文本文件)后,我通过

##代码##

To create resources, see How to create and use resources in .NET.

要创建资源,请参阅如何在 .NET 中创建和使用资源