Menampilkan tanggal pembuatan


260

Saat ini saya memiliki aplikasi yang menampilkan nomor build di jendela judulnya. Itu bagus dan bagus kecuali itu tidak ada artinya bagi sebagian besar pengguna, yang ingin tahu apakah mereka memiliki versi terbaru - mereka cenderung menyebutnya sebagai "Kamis lalu" daripada versi 1.0.8.4321.

Rencananya adalah meletakkan tanggal pembuatan di sana sebagai gantinya - Jadi "App dibangun pada 21/10/2009" misalnya.

Saya kesulitan menemukan cara terprogram untuk menarik tanggal pembuatan sebagai string teks untuk digunakan seperti ini.

Untuk nomor build, saya menggunakan:

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

setelah mendefinisikan bagaimana itu muncul.

Saya ingin sesuatu seperti itu untuk tanggal kompilasi (dan waktu, untuk poin bonus).

Pointer di sini sangat dihargai (permisi pun jika perlu), atau solusi yang lebih rapi ...


2
Saya mencoba cara yang disediakan untuk mendapatkan data rakitan yang berfungsi dalam skenario sederhana tetapi jika dua rakitan digabung bersama saya tidak mendapatkan waktu pembuatan yang benar, itu satu jam di masa mendatang .. ada saran?

Jawaban:


356

Jeff Atwood memiliki beberapa hal untuk dikatakan tentang masalah ini dalam Menentukan Build Date dengan cara yang sulit .

Metode yang paling dapat diandalkan ternyata mengambil cap waktu tautan dari header PE yang tertanam dalam file yang dapat dieksekusi - beberapa kode C # (oleh Joe Spivey) untuk itu dari komentar di artikel 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;
}

Contoh penggunaan:

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

UPDATE: Metode ini bekerja untuk .Net Core 1.0, tetapi berhenti bekerja setelah .Net Core 1.1 rilis (memberikan tahun acak dalam kisaran 1900-2020)


8
Saya telah mengubah nada suara saya tentang ini, saya masih sangat berhati-hati ketika menggali header PE acutal. Tapi sejauh yang saya tahu, hal PE ini jauh lebih dapat diandalkan daripada menggunakan nomor versi, selain itu saya tidak dapat menetapkan nomor versi terpisah dari tanggal pembuatan.
John Leidegren

6
Saya suka ini dan saya menggunakannya, tetapi yang kedua untuk baris terakhir dengan .AddHours()agak hackish dan (saya pikir) tidak akan mempertimbangkan DST. Jika Anda menginginkannya dalam waktu lokal, Anda sebaiknya menggunakan pembersih itu dt.ToLocalTime();. Bagian tengah juga bisa sangat disederhanakan dengan using()blok.
JLRishe

6
Ya, ini berhenti bekerja untuk saya dengan .net core juga (1940-an, 1960-an, dll)
eoleary

7
Sementara penggunaan header PE mungkin tampak pilihan yang baik hari ini, perlu dicatat bahwa MS bereksperimen dengan build deterministic (yang akan membuat header ini tidak berguna) dan bahkan mungkin menjadikannya default di versi kompiler C # mendatang (untuk alasan yang baik). Baik dibaca: blog.paranoidcoding.com/2016/04/05/... dan inilah jawaban yang terkait dengan .NET Core (TLDR: "ini sesuai desain"): developercommunity.visualstudio.com/content/problem/35873/…
Paweł Bulwan

13
Bagi mereka yang menemukan ini tidak lagi berfungsi, masalah ini bukan masalah .NET Core. Lihat jawaban saya di bawah ini tentang default parameter build baru yang dimulai dengan Visual Studio 15.4.
Tom

107

Tambahkan di bawah ini untuk baris perintah acara pra-bangun:

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

Tambahkan file ini sebagai sumber daya, sekarang Anda memiliki string 'BuildDate' di sumber daya Anda.

Untuk membuat sumber daya, lihat Cara membuat dan menggunakan sumber daya di .NET .


4
+1 dari saya, sederhana dan efektif. Saya bahkan berhasil mendapatkan nilai dari file dengan baris kode seperti ini: String buildDate = <MyClassLibraryName> .Properties.Resources.BuildDate
davidfrancis

11
Pilihan lain adalah membuat kelas: (harus memasukkan dalam proyek setelah pertama kali Anda mengkompilasinya) -> echo namespace My.app.namespace {public static class Build {public static string Timestamp = "% DATE%% TIME%" .Substring (0,16);}}> "$ (ProjectDir) \ BuildTimestamp.cs" - - - -> lalu dapat memanggilnya dengan Build.Timestamp
FabianSilva

9
Ini adalah solusi yang sangat baik. Satu-satunya masalah adalah bahwa% date% dan% time% variabel baris perintah dilokalkan, sehingga hasilnya akan bervariasi tergantung pada bahasa Windows pengguna.
VS

2
+1, ini adalah metode yang lebih baik daripada membaca header PE - karena ada beberapa skenario di mana itu tidak akan bekerja sama sekali (misalnya Windows Phone App)
Matt Whitfield

17
Pintar. Anda juga dapat menggunakan powershell untuk mendapatkan kontrol yang lebih tepat atas format, misalnya untuk mendapatkan datetime UTC yang diformat sebagai ISO8601: powershell -Command "((Get-Date) .ToUnniversalTime ()). ToString (\" s \ ") | Keluar File '$ (ProjectDir) Resources \ BuildDate.txt' "
dbruning

90

Jalan

Seperti yang ditunjukkan oleh @ c00000fd dalam komentar . Microsoft mengubah ini. Dan sementara banyak orang tidak menggunakan versi terbaru dari kompiler mereka, saya curiga perubahan ini membuat pendekatan ini menjadi buruk. Dan walaupun ini adalah latihan yang menyenangkan, saya akan merekomendasikan orang-orang untuk menanamkan tanggal pembuatan ke dalam biner mereka melalui cara lain yang diperlukan jika penting untuk melacak tanggal pembuatan dari biner itu sendiri.

Ini dapat dilakukan dengan beberapa pembuatan kode sepele yang mungkin merupakan langkah pertama dalam skrip build Anda. Itu, dan fakta bahwa alat ALM / Build / DevOps banyak membantu dengan ini dan harus lebih disukai daripada yang lain.

Saya meninggalkan sisa jawaban ini di sini hanya untuk tujuan historis.

Cara baru

Saya berubah pikiran tentang ini, dan saat ini menggunakan trik ini untuk mendapatkan tanggal pembuatan yang benar.

#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\0\0"
            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

Cara lama

Nah, bagaimana Anda menghasilkan angka membangun? Visual Studio (atau kompiler C #) benar-benar memberikan angka pembuatan dan revisi otomatis jika Anda mengubah atribut AssemblyVersion ke mis.1.0.*

Apa yang akan terjadi adalah bahwa bangunannya akan sama dengan jumlah hari sejak 1 Januari 2000 waktu setempat, dan untuk revisi sama dengan jumlah detik sejak tengah malam waktu setempat, dibagi 2.

lihat Konten Komunitas, Nomor Pembuatan dan Revisi Otomatis

mis. AssemblyInfo.cs

[assembly: AssemblyVersion("1.0.*")] // important: use wildcard for build and revision numbers!

SampleCode.cs

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)

3
Saya baru saja menambahkan satu jam jikaTimeZone.CurrentTimeZone.IsDaylightSavingTime(buildDateTime) == true
e4rthdog

2
Sayangnya saya menggunakan pendekatan ini tanpa benar-benar memeriksanya, dan itu menggigit kita dalam produksi. Masalahnya adalah bahwa ketika kompiler JIT menendang informasi header PE diubah. Karena itu downvote. Sekarang saya akan melakukan 'penelitian' yang tidak dibutuhkan untuk menjelaskan mengapa kami melihat tanggal pemasangan sebagai tanggal pembuatan.
Jason D

8
@JasonD Dalam semesta apa masalah Anda entah bagaimana menjadi masalah saya? Bagaimana Anda membenarkan downvote hanya karena Anda mengalami masalah yang implementasi ini tidak mempertimbangkan. Anda dapat ini gratis dan Anda mengujinya dengan buruk. Juga apa yang membuat Anda percaya bahwa tajuk sedang ditulis ulang oleh kompiler JIT? Apakah Anda membaca informasi ini dari memori proses atau dari file?
John Leidegren

6
Saya perhatikan bahwa jika Anda menjalankan aplikasi web, properti .Codebase tampaknya berupa URL (file: // c: /path/to/binary.dll). Ini menyebabkan panggilan File.Exists gagal. Menggunakan "assembly.Location" alih-alih properti CodeBase menyelesaikan masalah untuk saya.
mdryden

2
@JohnLeidegren: Jangan mengandalkan header Windows PE untuk itu. Karena Windows 10 dan build yang dapat direproduksi , IMAGE_FILE_HEADER::TimeDateStampbidang ini diatur ke angka acak dan tidak lagi dicap waktu.
c00000fd

51

Tambahkan di bawah ini untuk baris perintah acara pra-bangun:

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

Tambahkan file ini sebagai sumber daya, sekarang Anda memiliki string 'BuildDate' di sumber daya Anda.

Setelah memasukkan file ke Resource (sebagai file teks publik), saya mengaksesnya melalui

string strCompTime = Properties.Resources.BuildDate;

Untuk membuat sumber daya, lihat Cara membuat dan menggunakan sumber daya di .NET .


1
@ DavidGorsline - penurunan komentar sudah benar karena mengutip jawaban lain ini . Saya memiliki reputasi yang tidak cukup untuk mengembalikan perubahan Anda, jika tidak saya akan melakukannya sendiri.
Wai Ha Lee

1
@ Hai Ha Lee - a) jawaban yang Anda kutip tidak memberikan kode untuk benar-benar mengambil tanggal / waktu kompilasi. b) pada saat itu saya tidak memiliki reputasi yang cukup untuk menambahkan komentar pada jawaban itu (yang akan saya lakukan), hanya untuk mengirim. jadi c) saya memposting memberikan jawaban penuh sehingga orang bisa mendapatkan semua detail dalam satu area ..
brewmanz

Jika Anda melihat %te% bukan% date%, periksa di sini: developercommunity.visualstudio.com/content/problem/237752/… Singkatnya, lakukan ini: echo% 25tanggal% 25% 25waktu% 25
Qodex

41

Salah satu pendekatan yang saya kagum belum ada yang disebutkan adalah menggunakan T4 Text Templates untuk pembuatan kode.

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

Pro:

  • Independen lokal
  • Memungkinkan lebih dari sekedar waktu kompilasi

Cons:


1
Jadi, ini sekarang jawaban terbaik. 324 poin untuk pergi sebelum menjadi jawaban terpilih :). Stackoverflow membutuhkan cara untuk menunjukkan pendaki tercepat.
pauldendulk

1
@ Pauldendulk, tidak akan banyak membantu, karena jawaban yang paling banyak dipilih dan jawaban yang diterima hampir selalu mengambil suara tercepat. Jawaban yang diterima untuk pertanyaan ini memiliki +60 / -2 sejak saya memposting jawaban ini.
Peter Taylor

Saya yakin Anda perlu menambahkan .ToString () ke Kutu Anda (saya mendapatkan kesalahan kompilasi jika tidak). Yang mengatakan, saya mengalami kurva belajar yang curam di sini, dapatkah Anda menunjukkan cara MENGGUNAKAN ini dalam program utama juga?
Andy

@Andy, Anda benar tentang ToString (). Penggunaan itu adil Constants.CompilationTimestampUtc. Jika VS tidak menghasilkan file C # dengan kelas maka Anda perlu mencari cara untuk melakukannya, tetapi jawabannya tergantung pada (paling tidak) versi VS dan jenis file csproj, jadi itu adalah terlalu banyak detail untuk posting ini.
Peter Taylor

1
Jika yang lain bertanya-tanya, inilah yang diperlukan untuk membuatnya bekerja pada VS 2017: Saya harus membuat ini sebagai templat T4 Desain Waktu (Butuh beberapa saat untuk mencari tahu, saya menambahkan templat Preprocessor terlebih dahulu). Saya juga harus memasukkan rakitan ini: Microsoft.VisualStudio.TextTemplating.Interfaces.10.0 sebagai referensi untuk proyek. Akhirnya templat saya harus menyertakan "using System;" sebelum namespace, atau referensi ke DateTime gagal.
Andy

20

Mengenai teknik menarik info tanggal / versi build dari byte header PE assembly, Microsoft telah mengubah parameter build default yang dimulai dengan Visual Studio 15.4. Default baru mencakup kompilasi deterministik, yang membuat stempel waktu yang valid dan nomor versi yang ditambahkan secara otomatis menjadi bagian dari masa lalu. Bidang cap waktu masih ada tetapi diisi dengan nilai permanen yang merupakan hash dari sesuatu atau lainnya, tetapi tidak ada indikasi waktu pembuatan.

Beberapa latar belakang rinci di sini

Bagi mereka yang memprioritaskan cap waktu yang berguna di atas kompilasi deterministik, ada cara untuk mengganti default baru. Anda dapat menyertakan tag dalam file .csproj dari kumpulan yang diminati sebagai berikut:

  <PropertyGroup>
      ...
      <Deterministic>false</Deterministic>
  </PropertyGroup>

Pembaruan: Saya mendukung solusi template teks T4 yang dijelaskan dalam jawaban lain di sini. Saya menggunakannya untuk menyelesaikan masalah saya dengan bersih tanpa kehilangan manfaat dari kompilasi deterministik. Satu peringatan tentang hal itu adalah bahwa Visual Studio hanya menjalankan kompiler T4 ketika file .tt disimpan, bukan pada waktu membangun. Ini bisa menjadi canggung jika Anda mengecualikan hasil .cs dari kontrol sumber (karena Anda mengharapkannya dihasilkan) dan pengembang lain memeriksa kode. Tanpa resetting, mereka tidak akan memiliki file .cs. Ada paket di nuget (saya pikir disebut AutoT4) yang membuat bagian kompilasi T4 dari setiap build. Saya belum menghadapi solusi untuk ini selama penyebaran produksi, tetapi saya mengharapkan sesuatu yang serupa untuk memperbaikinya.


Ini memecahkan masalah saya di SLN yang menggunakan jawaban tertua.
pauldendulk

Hati-hati Anda tentang T4 benar-benar adil, tetapi perhatikan bahwa sudah ada dalam jawaban saya.
Peter Taylor

15

Saya hanya pemula C # jadi mungkin jawaban saya terdengar konyol - Saya menampilkan tanggal pembuatan dari tanggal file yang dapat dieksekusi terakhir ditulis ke:

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

Saya mencoba menggunakan metode File.GetCreationTime tetapi mendapat hasil aneh: tanggal dari perintah adalah 2012-05-29, tetapi tanggal dari Window Explorer menunjukkan 2012-05-23. Setelah mencari perbedaan ini, saya menemukan bahwa file itu mungkin dibuat pada 2012-05-23 (seperti yang ditunjukkan oleh Windows Explorer), tetapi disalin ke folder saat ini pada 2012-05-29 (seperti yang ditunjukkan oleh perintah File.GetCreationTime) - jadi untuk berada di sisi aman saya menggunakan perintah File.GetLastWriteTime.

Zalek


4
Saya tidak yakin apakah ini bukti peluru dari menyalin yang dapat dieksekusi di drive / komputer / jaringan.
Stealth Rabbi

ini adalah hal pertama yang terlintas dalam pikiran tetapi Anda tahu itu tidak dapat diandalkan ada banyak perangkat lunak yang digunakan untuk memindahkan file melalui jaringan yang tidak memperbarui atribut setelah mengunduh, saya akan pergi dengan jawaban @ Abdurrahim.
Mubashar

Saya tahu ini sudah tua, tetapi saya baru saja menemukan dengan kode yang sama bahwa proses INSTALL (setidaknya saat menggunakan clickonce) memperbarui waktu file perakitan. Sangat tidak berguna. Tidak yakin itu akan berlaku untuk solusi ini.
bobwki

Anda mungkin benar-benar menginginkannya LastWriteTime, karena itu secara akurat mencerminkan waktu ketika file yang dapat dieksekusi benar-benar diperbarui.
David R Tribble

Maaf tetapi waktu penulisan file yang dapat dieksekusi bukanlah indikasi yang dapat diandalkan tentang waktu pembuatan. Cap waktu file dapat ditulis ulang karena semua jenis hal yang berada di luar lingkup pengaruh Anda.
Tom

15

Banyak jawaban bagus di sini tapi saya merasa saya bisa menambahkan sendiri karena kesederhanaan, kinerja (membandingkan dengan solusi terkait sumber daya) lintas platform (bekerja dengan Net Core juga) dan menghindari alat pihak ke-3. Cukup tambahkan target msbuild ini ke csproj.

<Target Name="Date" BeforeTargets="CoreCompile">
    <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>

dan sekarang Anda miliki Builtin.CompileTimeatau new DateTime(Builtin.CompileTime, DateTimeKind.Utc)jika Anda membutuhkannya.

ReSharper tidak akan menyukainya. Anda dapat mengabaikannya atau menambahkan kelas parsial ke proyek juga, tetapi tetap berhasil.


Saya dapat membangun dengan ini dan mengembangkan secara lokal (menjalankan situs web) di ASP.NET Core 2.1 tetapi penerbitan penyebaran web dari VS 2017 gagal dengan kesalahan "Nama 'Builtin' tidak ada dalam konteks saat ini". TAMBAHAN: Jika saya mengakses Builtin.CompileTimedari tampilan Razor.
Jeremy Cook

Dalam hal ini saya pikir Anda hanya perlu BeforeTargets="RazorCoreCompile"tetapi hanya sementara ini dalam proyek yang sama
Dmitry Gusarov

keren, tapi bagaimana kita merujuk ke objek yang dihasilkan? bagi saya jawabannya adalah bagian yang hilang ...
Matteo

1
@ Matteo, seperti yang disebutkan dalam jawaban, Anda dapat menggunakan "Builtin.CompileTime" atau "DateTime baru (Builtin.CompileTime, DateTimeKind.Utc)". Visual Studio IntelliSense mampu melihat ini segera. ReSharper lama dapat mengeluh dalam waktu desain, tetapi sepertinya mereka memperbaikinya dalam versi baru. clip2net.com/s/46rgaaO
Dmitry Gusarov

Saya menggunakan versi ini jadi tidak perlu kode tambahan untuk mendapatkan tanggal. Juga resharper tidak mengeluh dengan versi terbarunya. <WriteLinesToFile File = "$ (IntermediateOutputPath) BuildInfo.cs" Lines = "menggunakan System% 3B internal partial partial class BuildInfo {public static long DateBuiltTicks = $ ([System.DateTime] :: UtcNow.Ticks)% 3B public DateTime DateBuilt statis => DateTime baru (DateBuiltTicks, DateTimeKind.Utc)% 3B} "Timpa =" true "/>
Softlion

13

Untuk proyek .NET Core, saya mengadaptasi jawaban Postlagerkarte untuk memperbarui bidang Hak Cipta perakitan dengan tanggal pembuatan.

Edit csproj secara langsung

Berikut ini dapat ditambahkan langsung ke yang pertama PropertyGroupdi csproj:

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

Alternatif: Properti Proyek Visual Studio

Atau tempelkan ekspresi bagian dalam langsung ke bidang Hak Cipta di bagian Paket properti proyek di Visual Studio:

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

Ini bisa sedikit membingungkan, karena Visual Studio akan mengevaluasi ekspresi dan menampilkan nilai saat ini di jendela, tetapi juga akan memperbarui file proyek dengan tepat di belakang layar.

Solusi-lebar melalui Directory.Build.props

Anda dapat memasukkan <Copyright>elemen di atas ke dalam Directory.Build.propsfile di root solusi Anda, dan menerapkannya secara otomatis ke semua proyek dalam direktori, dengan asumsi setiap proyek tidak memberikan nilai Hak Cipta sendiri.

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

Directory.Build.props: Sesuaikan bangunan Anda

Keluaran

Contoh ekspresi akan memberi Anda hak cipta seperti ini:

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

Pengambilan

Anda dapat melihat informasi hak cipta dari properti file di Windows, atau ambil saat runtime:

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

Console.WriteLine(version.LegalCopyright);

11

Metode di atas dapat di-tweak untuk rakitan yang sudah dimuat dalam proses dengan menggunakan gambar file dalam memori (bukan membaca ulang dari penyimpanan):

using System;
using System.Runtime.InteropServices;
using Assembly = System.Reflection.Assembly;

static class Utils
{
    public static DateTime GetLinkerDateTime(this Assembly assembly, TimeZoneInfo tzi = null)
    {
        // Constants related to the Windows PE file format.
        const int PE_HEADER_OFFSET = 60;
        const int LINKER_TIMESTAMP_OFFSET = 8;

        // Discover the base memory address where our assembly is loaded
        var entryModule = assembly.ManifestModule;
        var hMod = Marshal.GetHINSTANCE(entryModule);
        if (hMod == IntPtr.Zero - 1) throw new Exception("Failed to get HINSTANCE.");

        // Read the linker timestamp
        var offset = Marshal.ReadInt32(hMod, PE_HEADER_OFFSET);
        var secondsSince1970 = Marshal.ReadInt32(hMod, offset + LINKER_TIMESTAMP_OFFSET);

        // Convert the timestamp to a DateTime
        var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        var linkTimeUtc = epoch.AddSeconds(secondsSince1970);
        var dt = TimeZoneInfo.ConvertTimeFromUtc(linkTimeUtc, tzi ?? TimeZoneInfo.Local);
        return dt;
    }
}

Yang ini berfungsi dengan baik, bahkan untuk kerangka kerja 4.7 Penggunaan: Utils.GetLinkerDateTime (Assembly.GetExecutingAssembly (), null))
real_yggdrasil

Bagus sekali! Terima kasih!
bobwki

10

Bagi siapa pun yang perlu mendapatkan waktu kompilasi di Windows 8 / Windows Phone 8:

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

Bagi siapa pun yang perlu mendapatkan waktu kompilasi di Windows Phone 7:

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

CATATAN: Dalam semua kasus Anda berjalan di kotak pasir, sehingga Anda hanya akan bisa mendapatkan waktu kompilasi rakitan yang Anda gunakan dengan aplikasi Anda. (yaitu ini tidak akan bekerja pada apa pun di GAC).


Begini cara Anda mendapatkan Assembly di WP 8.1:var assembly = typeof (AnyTypeInYourAssembly).GetTypeInfo().Assembly;
André Fiedler

Bagaimana jika Anda ingin menjalankan kode Anda di kedua sistem? - Apakah salah satu dari metode ini berlaku untuk kedua platform?
bvdb

10

Pada 2018 beberapa solusi di atas tidak berfungsi lagi atau tidak bekerja dengan .NET Core.

Saya menggunakan pendekatan berikut yang sederhana dan berfungsi untuk proyek .NET Core 2.0 saya.

Tambahkan berikut ini ke .csproj Anda di dalam PropertyGroup:

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

Ini mendefinisikan PropertyFunction yang bisa Anda akses di perintah pre build Anda.

Pra-bangun Anda terlihat seperti ini

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

Setel properti BuildTimeStamp.txt ke sumber daya Tertanam.

Sekarang Anda dapat membaca cap waktu seperti ini

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

Hanya menghasilkan BuildTimeStamp.txt dari acara pra-bangun menggunakan perintah skrip batch juga berfungsi. Perhatikan bahwa Anda membuat kesalahan di sana: Anda harus mengelilingi target Anda dalam tanda kutip (misalnya "$(ProjectDir)BuildTimeStamp.txt") atau itu akan pecah ketika ada spasi di nama folder.
Nyerguds

Mungkin masuk akal untuk menggunakan format waktu budaya invarian. Seperti ini: $([System.DateTime]::Now.tostring("MM/dd/yyyy HH:mm:ss"))bukan$([System.DateTime]::Now)
Ivan Kochurkin

9

Opsi yang tidak dibahas di sini adalah untuk memasukkan data Anda sendiri ke AssemblyInfo.cs, bidang "AssemblyInformationalVersion" tampaknya tepat - kami memiliki beberapa proyek di mana kami melakukan sesuatu yang mirip dengan langkah pembangunan (namun saya tidak sepenuhnya senang dengan cara yang berhasil jadi tidak benar-benar ingin mereproduksi apa yang kita punya).

Ada artikel tentang subjek di codeproject: http://www.codeproject.com/KB/dotnet/Customizing_csproj_files.aspx


6

Saya membutuhkan solusi universal yang bekerja dengan proyek NETStandard di platform apa pun (iOS, Android, dan Windows.) Untuk mencapai ini, saya memutuskan untuk secara otomatis menghasilkan file CS melalui skrip PowerShell. Berikut ini skrip 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

Simpan file PowerScript sebagai GenBuildDate.ps1 dan tambahkan proyek Anda. Terakhir, tambahkan baris berikut ke acara Pra-Bangun Anda:

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

Pastikan BuildDate.cs termasuk dalam proyek Anda. Bekerja seperti juara di OS apa pun!


1
Anda juga dapat menggunakan ini untuk mendapatkan nomor revisi SVN menggunakan alat baris perintah svn. Saya telah melakukan sesuatu yang mirip dengan ini dengan itu.
user169771

5

Saya hanya melakukan:

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

1
Menariknya, jika dijalankan dari debug, tanggal 'benar' adalah GetLastAccessTime ()
balint

4

Anda dapat menggunakan proyek ini: https://github.com/dwcullop/BuildInfo

Ini memanfaatkan T4 untuk mengotomatiskan timestamp tanggal pembuatan. Ada beberapa versi (cabang berbeda) termasuk satu yang memberi Anda Git Hash dari cabang yang sedang diperiksa, jika Anda menyukai hal-hal semacam itu.

Pengungkapan: Saya menulis modul.


3

Pendekatan lain yang ramah PCL adalah menggunakan tugas inline MSBuild untuk mengganti waktu pembuatan menjadi string yang dikembalikan oleh properti di aplikasi. Kami menggunakan pendekatan ini dengan sukses di aplikasi yang memiliki proyek Xamarin.Forms, Xamarin.Android, dan Xamarin.iOS.

EDIT:

Disederhanakan dengan memindahkan semua logika ke dalam SetBuildDate.targetsfile, dan menggunakan Regexalih-alih mengganti string sederhana sehingga file dapat dimodifikasi oleh setiap build tanpa "reset".

Definisi tugas inline MSBuild (disimpan dalam file SetBuildDate.targets lokal untuk proyek Xamarin.Forms untuk contoh ini):

<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' ToolsVersion="12.0">

  <UsingTask TaskName="SetBuildDate" TaskFactory="CodeTaskFactory" 
    AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll">
    <ParameterGroup>
      <FilePath ParameterType="System.String" Required="true" />
    </ParameterGroup>
    <Task>
      <Code Type="Fragment" Language="cs"><![CDATA[

        DateTime now = DateTime.UtcNow;
        string buildDate = now.ToString("F");
        string replacement = string.Format("BuildDate => \"{0}\"", buildDate);
        string pattern = @"BuildDate => ""([^""]*)""";
        string content = File.ReadAllText(FilePath);
        System.Text.RegularExpressions.Regex rgx = new System.Text.RegularExpressions.Regex(pattern);
        content = rgx.Replace(content, replacement);
        File.WriteAllText(FilePath, content);
        File.SetLastWriteTimeUtc(FilePath, now);

   ]]></Code>
    </Task>
  </UsingTask>

</Project>

Menjalankan tugas inline di atas dalam file camarx Xamarin.Forms di target BeforeBuild:

  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
       Other similar extension points exist, see Microsoft.Common.targets.  -->
  <Import Project="SetBuildDate.targets" />
  <Target Name="BeforeBuild">
    <SetBuildDate FilePath="$(MSBuildProjectDirectory)\BuildMetadata.cs" />
  </Target>

The FilePathproperti diatur ke BuildMetadata.csfile dalam proyek Xamarin.Forms yang berisi kelas sederhana dengan properti string BuildDate, di mana waktu membangun akan diganti:

public class BuildMetadata
{
    public static string BuildDate => "This can be any arbitrary string";
}

Tambahkan file ini BuildMetadata.cske proyek. Ini akan dimodifikasi oleh setiap build, tetapi dengan cara yang memungkinkan build berulang (replacements berulang), jadi Anda dapat memasukkan atau menghilangkannya dalam kontrol sumber seperti yang diinginkan.


2

Anda bisa menggunakan acara post-build proyek untuk menulis file teks ke direktori target Anda dengan datetime saat ini. Anda kemudian bisa membaca nilainya pada saat run-time. Agak aneh, tetapi harus berhasil.



2

Pembaruan kecil pada jawaban "Cara Baru" dari Jhon.

Anda perlu membangun jalur alih-alih menggunakan string CodeBase saat bekerja dengan ASP.NET/MVC

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

1

Anda bisa meluncurkan langkah ekstra dalam proses pembuatan yang menulis cap tanggal ke file yang kemudian dapat ditampilkan.

Pada tab properti projek lihat pada tab build event. Ada opsi untuk menjalankan perintah build pre atau post.


1

Saya menggunakan saran Gus Dur. Namun, sepertinya memberikan format waktu yang aneh dan juga menambahkan singkatan untuk hari itu sebagai bagian dari tanggal pembuatan; contoh: Minggu 12/24/2017 13: 21: 05.43. Saya hanya perlu tanggal saja jadi saya harus menghilangkan sisanya menggunakan substring.

Setelah menambahkan echo %date% %time% > "$(ProjectDir)\Resources\BuildDate.txt"ke acara pra-bangun, saya hanya melakukan hal berikut:

string strBuildDate = YourNamespace.Properties.Resources.BuildDate;
string strTrimBuildDate = strBuildDate.Substring(4).Remove(10);

Kabar baiknya di sini adalah bahwa itu berhasil.


Solusi yang sangat sederhana. Saya suka itu. Dan jika formatnya mengganggu, ada cara untuk mendapatkan format yang lebih baik dari baris perintah.
Nyerguds

0

Jika ini adalah aplikasi windows, Anda bisa menggunakan jalur eksekusi aplikasi: new System.IO.FileInfo (Application.ExecutablePath) .LastWriteTime.ToString ("yyyy.MM.dd")


2
Sudah dan jawab menggunakan ini, dan juga tidak benar-benar anti peluru.
crashmstr

0

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


Bukankah ini melakukan hal yang sama dengan jawaban lainnya ?
Wai Ha Lee
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.