parse - formato fecha datetime c#
Viendo la fecha de construcción (22)
Actualmente tengo una aplicación que muestra el número de compilación en su ventana de título. Eso es bueno y bueno, excepto que no significa nada para la mayoría de los usuarios, que quieren saber si tienen la última versión, tienden a referirse a ella como "el último jueves" en lugar de la versión 1.0.8.4321.
El plan es poner la fecha de compilación allí en su lugar, por lo que, por ejemplo, "Aplicación creada el 21/10/2009".
Estoy luchando por encontrar una manera programática de extraer la fecha de construcción como una cadena de texto para usar de esta manera.
Para el número de compilación, utilicé:
Assembly.GetExecutingAssembly().GetName().Version.ToString()
después de definir cómo surgieron esos.
Me gustaría algo así para la fecha de compilación (y la hora, para los puntos de bonificación).
Los punteros aquí son muy apreciados (disculpe, si es apropiado), o soluciones más limpias ...
La nueva forma
Cambié de opinión sobre esto y actualmente uso este truco para obtener la fecha de compilación correcta.
#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
El viejo camino
Bueno, ¿cómo generar números de compilación? Visual Studio (o el compilador de C #) en realidad proporciona números de compilación y revisión automáticos si cambia el atributo AssemblyVersion a, por ejemplo, 1.0.*
Lo que sucederá es que la compilación será igual al número de días desde el 1 de enero de 2000, hora local, y para que la revisión sea igual al número de segundos desde la medianoche hora local, dividido por 2.
Ver contenido de la comunidad, compilación automática y números de revisión.
por ejemplo, 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)
Agregue a continuación a la línea de comando de evento de precompilación
echo %date% %time% > "$(ProjectDir)/Resources/BuildDate.txt"
Agregue este archivo como recurso, ahora tiene la cadena ''BuildDate'' en sus recursos.
Para crear recursos, vea Cómo crear y usar recursos en .NET .
Agregue a continuación a la línea de comando de evento de precompilación:
echo %date% %time% > "$(ProjectDir)/Resources/BuildDate.txt"
Agregue este archivo como recurso, ahora tiene la cadena ''BuildDate'' en sus recursos.
Después de insertar el archivo en el Recurso (como archivo de texto público), accedí a él a través de
string strCompTime = Properties.Resources.BuildDate;
Para crear recursos, vea Cómo crear y usar recursos en .NET .
Con respecto a la técnica de extraer información de fecha / versión de compilación de los bytes de un encabezado PE de ensamblaje, Microsoft ha cambiado los parámetros de compilación predeterminados comenzando con Visual Studio 15.4. El nuevo valor predeterminado incluye la compilación determinista, que hace que una marca de tiempo válida y números de versión incrementados automáticamente sean cosa del pasado. El campo de marca de tiempo todavía está presente pero se llena con un valor permanente que es un hash de algo u otro, pero no una indicación del tiempo de construcción.
blog.paranoidcoding.com/2016/04/05/… blog.paranoidcoding.com/2016/04/05/…
Para aquellos que priorizan una marca de tiempo útil sobre la compilación determinista, hay una manera de anular el nuevo valor predeterminado. Puede incluir una etiqueta en el archivo .csproj del conjunto de interés de la siguiente manera:
<PropertyGroup>
...
<Deterministic>false</Deterministic>
</PropertyGroup>
Actualización: Respaldo la solución de plantilla de texto T4 descrita en otra respuesta aquí. Lo usé para resolver mi problema limpiamente sin perder el beneficio de la compilación determinista. Una advertencia al respecto es que Visual Studio solo ejecuta el compilador T4 cuando se guarda el archivo .tt, no en el momento de la compilación. Esto puede ser incómodo si excluye el resultado de .cs del control de origen (ya que espera que se genere) y otro desarrollador verifica el código. Sin volver a guardar, no tendrán el archivo .cs. Hay un paquete en nuget (creo que se llama AutoT4) que hace que la compilación de T4 sea parte de cada compilación. Todavía no he confrontado la solución a esto durante la implementación de producción, pero espero algo similar para hacerlo bien.
El método anterior se puede modificar para ensamblajes ya cargados dentro del proceso usando la imagen del archivo en la memoria (en lugar de volver a leerlo desde el almacenamiento):
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;
}
}
En 2018, algunas de las soluciones anteriores ya no funcionan o no funcionan con .NET Core.
Utilizo el siguiente enfoque, que es simple y funciona para mi proyecto .NET Core 2.0.
Agregue lo siguiente a su .csproj dentro del Grupo de propiedades:
<Today>$([System.DateTime]::Now)</Today>
Esto define un PropertyFunction que puede acceder en su comando de compilación previa.
Tu pre-compilación se ve así
echo $(today) > $(ProjectDir)BuildTimeStamp.txt
Establezca la propiedad de BuildTimeStamp.txt en el recurso incrustado.
Ahora puedes leer la marca de tiempo como esta
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();
}
}
}
Hay muchas respuestas excelentes aquí, pero creo que puedo agregar las mías debido a la simplicidad, el rendimiento (en comparación con las soluciones relacionadas con los recursos) multiplataforma (también funciona con Net Core) y evitar cualquier herramienta de terceros. Simplemente agregue este destino msbuild al 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>
y ahora tiene Builtin.CompileTime
o new DateTime(Builtin.CompileTime, DateTimeKind.Utc)
si lo necesita de esa manera.
A ReSharper no le va a gustar. Puedes ignorarlo o agregar una clase parcial al proyecto también, pero funciona de todos modos.
Jeff Atwood tuvo algunas cosas que decir acerca de este problema al determinar la fecha de construcción de la manera más difícil .
El método más confiable es recuperar la marca de tiempo del vinculador desde el encabezado PE incrustado en el archivo ejecutable, algo de código C # (por Joe Spivey) para eso desde los comentarios al artículo de 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;
}
Ejemplo de uso:
var linkTimeLocal = Assembly.GetExecutingAssembly().GetLinkerTime();
ACTUALIZACIÓN: el método funcionaba para .Net Core 1.0, pero dejó de funcionar después de la versión .Net Core 1.1 (da años aleatorios en el rango 1900-2020)
La opción que no se trata aquí es insertar sus propios datos en AssemblyInfo.cs, el campo "AssemblyInformationalVersion" parece apropiado. Tenemos un par de proyectos en los que estábamos haciendo algo similar como un paso de compilación (sin embargo, no estoy del todo satisfecho con el Así funciona, así que realmente no quiero reproducir lo que tenemos).
Hay un artículo sobre el tema en codeproject: http://www.codeproject.com/KB/dotnet/Customizing_csproj_files.aspx
Necesitaba una solución universal que funcionara con un proyecto NETStandard en cualquier plataforma (iOS, Android y Windows). Para lograr esto, decidí generar automáticamente un archivo CS a través de un script de PowerShell. Aquí está el script de 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
Guarde el archivo PowerScript como GenBuildDate.ps1 y agréguelo a su proyecto. Finalmente, agregue la siguiente línea a su evento Pre-Build:
powershell -File $(ProjectDir)GenBuildDate.ps1 -outputFile $(ProjectDir)BuildDate.cs
Asegúrese de que BuildDate.cs esté incluido en su proyecto. Funciona como un campeón en cualquier sistema operativo!
No estoy seguro, pero quizás el Build Incrementer ayude.
Para cualquier persona que necesite obtener el tiempo de compilación en 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);
}
}
}
Para cualquier persona que necesite obtener el tiempo de compilación en 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;
}
NOTA: En todos los casos, se está ejecutando en un entorno aislado, por lo que solo podrá obtener el tiempo de compilación de los ensamblados que implementa con su aplicación. (es decir, esto no funcionará en nada en el GAC).
Para los proyectos de .NET Core, adapté la respuesta de Postlagerkarte para actualizar el campo de Copyright del ensamblaje con la fecha de compilación.
Edita directamente csproj
Lo siguiente se puede agregar directamente al primer PropertyGroup
en el csproj:
<Copyright>Copyright © $([System.DateTime]::UtcNow.Year) Travis Troyer ($([System.DateTime]::UtcNow.ToString("s")))</Copyright>
Alternativa: Propiedades de proyecto de Visual Studio
O pegue la expresión interna directamente en el campo de Copyright en la sección Paquete de las propiedades del proyecto en Visual Studio:
Copyright © $([System.DateTime]::UtcNow.Year) Travis Troyer ($([System.DateTime]::UtcNow.ToString("s")))
Esto puede ser un poco confuso, ya que Visual Studio evaluará la expresión y mostrará el valor actual en la ventana, pero también actualizará el archivo del proyecto adecuadamente entre bambalinas.
En toda la solución a través de Directory.Build.props
Puede colocar el elemento <Copyright>
arriba en un archivo Directory.Build.props
en la raíz de su solución y hacer que se aplique automáticamente a todos los proyectos dentro del directorio, asumiendo que cada proyecto no proporciona su propio valor de Copyright.
<Project>
<PropertyGroup>
<Copyright>Copyright © $([System.DateTime]::UtcNow.Year) Travis Troyer ($([System.DateTime]::UtcNow.ToString("s")))</Copyright>
</PropertyGroup>
</Project>
Directory.Build.props: Personaliza tu compilación
Salida
La expresión de ejemplo le dará un derecho de autor como este:
Copyright © 2018 Travis Troyer (2018-05-30T14:46:23)
Recuperación
Puede ver la información de copyright de las propiedades del archivo en Windows o capturarla en tiempo de ejecución:
var version = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location);
Console.WriteLine(version.LegalCopyright);
Podría iniciar un paso adicional en el proceso de construcción que escriba una marca de fecha en un archivo que luego se puede mostrar.
En la pestaña de propiedades de proyectos mira la pestaña de eventos de compilación. Hay una opción para ejecutar un comando de compilación anterior o posterior.
Podría usar un evento posterior a la compilación del proyecto para escribir un archivo de texto en su directorio de destino con la fecha y hora actual. A continuación, podría leer el valor en tiempo de ejecución. Es un poco hacky, pero debería funcionar.
Puede usar este proyecto: https://github.com/dwcullop/BuildInfo
Aprovecha T4 para automatizar la fecha y hora de la fecha de construcción. Hay varias versiones (diferentes sucursales), incluida una que te da el Git Hash de la rama actualmente seleccionada, si te gusta ese tipo de cosas.
Divulgación: escribí el módulo.
Si se trata de una aplicación de Windows, puede usar la ruta ejecutable de la aplicación: new System.IO.FileInfo (Application.ExecutablePath) .LastWriteTime.ToString ("yyyy.MM.dd")
Solo soy novato en C #, así que quizás mi respuesta suene tonta: muestro la fecha de compilación desde la fecha en que se escribió el archivo ejecutable por última vez:
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());
Intenté usar el método File.GetCreationTime pero obtuve resultados extraños: la fecha del comando fue 2012-05-29, pero la fecha del Explorador de ventanas mostró 2012-05-23. Después de buscar esta discrepancia, descubrí que el archivo probablemente se creó el 2012-05-23 (como se muestra en el Explorador de Windows), pero se copió en la carpeta actual el 2012-05-29 (como se muestra en el comando File.GetCreationTime). para estar en el lado seguro Estoy usando el comando File.GetLastWriteTime.
Zalek
Un enfoque diferente, compatible con PCL sería utilizar una tarea en línea de MSBuild para sustituir el tiempo de construcción en una cadena devuelta por una propiedad en la aplicación. Estamos utilizando este enfoque con éxito en una aplicación que tiene proyectos Xamarin.Forms, Xamarin.Android y Xamarin.iOS.
EDITAR:
Se simplifica moviendo toda la lógica al archivo SetBuildDate.targets
y utilizando Regex
lugar de reemplazar cadenas simples para que cada compilación pueda modificar el archivo sin un "restablecimiento".
La definición de tarea en línea de MSBuild (guardada en un archivo SetBuildDate.targets local para el proyecto Xamarin.Forms para este ejemplo):
<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>
Invocando la tarea en línea anterior en el archivo csproj Xamarin.Forms en el destino 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>
La propiedad FilePath
se establece en un archivo BuildMetadata.cs
en el proyecto Xamarin.Forms que contiene una clase simple con una propiedad de cadena BuildDate
, en la que se sustituirá el tiempo de compilación:
public class BuildMetadata
{
public static string BuildDate => "This can be any arbitrary string";
}
Agregue este archivo BuildMetadata.cs
al proyecto. Será modificado por cada compilación, pero de una manera que permita compilaciones repetidas (reemplazos repetidos), por lo que puede incluirlo u omitirlo en el control de código fuente como lo desee.
Un enfoque que me sorprende que nadie haya mencionado aún es utilizar plantillas de texto T4 para la generación de código.
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System" #>
<#@ output extension=".g.cs" #>
namespace Foo.Bar
{
public static partial class Constants
{
public static DateTime CompilationTimestampUtc { get { return new DateTime(<# Write(DateTime.UtcNow.Ticks); #>L, DateTimeKind.Utc); } }
}
}
Pros:
- Locale-independiente
- Permite mucho más que el tiempo de compilación.
Contras:
- Solo aplicable a las bibliotecas donde controlas la fuente.
- Requiere configurar su proyecto (y el servidor de compilación, si eso no lo recoge) para ejecutar la plantilla en un paso previo a la compilación . (Ver también T4 sin VS ).
Una pequeña actualización de la respuesta "New Way" de Jhon.
Debe crear la ruta en lugar de usar la cadena de CodeBase cuando trabaje con ASP.NET/MVC
var codeBase = assembly.GetName().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
Utilicé la sugerencia de Abdurrahim. Sin embargo, pareció dar un formato de hora extraño y también agregó la abreviatura del día como parte de la fecha de construcción; Ejemplo: dom 24/12/2017 13: 21: 05.43. Solo necesitaba la fecha, así que tuve que eliminar el resto usando subcadenas.
Después de agregar echo %date% %time% > "$(ProjectDir)/Resources/BuildDate.txt"
al evento de construcción previa, solo hice lo siguiente:
string strBuildDate = YourNamespace.Properties.Resources.BuildDate;
string strTrimBuildDate = strBuildDate.Substring(4).Remove(10);
La buena noticia aquí es que funcionó.