now - timespan c#
Un objeto Timespan real con.Years &.Months (7)
Considere los siguientes 2 escenarios: Escenario 1). Hoy es el 1 de mayo de 2012, y el Escenario 2). Hoy es 1 de septiembre de 2012.
Ahora, considere que escribimos en nuestra página web lo siguiente acerca de un comentario que alguien dejó: "Este comentario fue escrito hace 3 meses y 12 días". La cantidad de días en ambos escenarios SIEMPRE será diferente aunque la declaración sea exactamente la misma. En el Escenario 1, "3 meses y 12 días" equivaldrían a 102 days
. Sin embargo, en el Escenario 2, "3 meses y 12 días" serían 104 days
.
Ahora, para tomar mi punto, usemos un ejemplo diferente y digamos que alguien dejó un comentario en nuestro sitio el 30 de enero de 2013, y hoy es 10 de marzo de 2013. Nuestro objeto TimeSpan real necesita conocer esta fecha relativa, y puede figurar fuera de lo siguiente:
- Que hay 10 días en marzo,
- Que hay 1 día en enero (contando del 30 al 31).
- Que el mes de febrero es un mes, independientemente de la cantidad de días que haya (a pesar de que son 28 días).
Entonces, significaría 10 días + 1 día + 1 mes en total, traduciéndose a This comment was posted 1 Month and 11 Days ago
.
Ahora, si usó el objeto TimeSpan estilo MS (o cualquier objeto TimeSpan en cualquier idioma), le daría el número de días del 30 de enero al 10 de marzo (39 días) y porque el objeto TimeSpan no almacena la fecha relativa. (la fecha base / inicial que restamos para obtener el TimeSpan), si le pregunta cuántos meses y días ha pasado, asumirá que hay 30 días en un mes, o incluso peor, el promedio que es mayor a 30 días y devuelve el resto en días, por lo que para llegar a los 39 días, te dirá que han pasado 1 mes y 9 días y recibirás This comment was posted 1 Month and 9 Days ago
. Recuerde, ambos escenarios tienen la misma fecha de inicio y la misma fecha actual / final, sí, el objeto Microsoft TimeSpan, al no permitirnos decir que el mes de febrero de 2013 debe considerarse, nos ha dado un TimeSpan completamente diferente, desactivado por un todo 2 días. En efecto, nos ha mentido.
El problema es que la gente creerá en esto y quién sabe qué percepciones pueden tener, cómo pueden cambiar sus percepciones del pasado y las decisiones y elecciones de vida que pueden hacer al tratar de reconstruir eventos dentro del pasado dentro de sus propias mentes, aunque nunca notar o comprender el inconveniente y la falla inherente de representar el tiempo que está tan presente en todo el mundo hoy. No entenderán que los lenguajes de programación no se dan cuenta (o les importa) que el último mes tuvo 31 días en él, opuesto a 30, 29 o 28, o viceversa, y que esto se suma cuando aumenta el Tiempo.
Este es el problema en el corazón de esta publicación. Entiendo que a la mayoría de las personas no les importará esta diferencia (pero asegúrese de que algunos de nosotros lo hagamos, y no podemos tener esto a nuestras espaldas), y si esto no le molesta, eso está bien. Desearía que no me molestara, me habría ahorrado algo de tiempo, estrés y desilusión. Si esto no le molesta, puede usar la función para la visualización textual eficiente de la hora relativa (personalizable de 1 a 6 nodos desde segundos hasta años), en lugar de usarla para la precisión generalmente despreciable que proporciona.
Para mi decepción, me di cuenta de que no hay un objetivo de tiempo real, si obtienes un intervalo de tiempo, y haces un .years
o un .months
, no obtendrás nada, solo obtendrás .days
y menos porque un objeto timeSpan no lleva cualquier cosa para decir en qué mes o año se creó timeSpan. Por lo tanto, nunca sabrá realmente cuántos meses han pasado desde que los días de cada mes varían en un año e incluso más en un año bisiesto.
En respuesta a esto, publicaré una función que desarrollé para obtener lecturas EXACTAS y poder devolver cosas como las siguientes en mi página web ASP.NET ...
Publicado hace 4 años, 3 meses, 14 días, 15 horas, 18 minutos y 24 segundos
Pensé que habría un ...
timeSpan.GetActualNumberOf[Months/Days/Hours/etc]
(por supuesto, debe proporcionarse la fecha base)
... escriba el método en este tipo de datos, pero no fue así.
Todo lo que realmente tendrías que hacer es crear otra propiedad en el objeto timeSpan para darle una fecha base en la cual se calculó la diferencia, entonces la encantadora cadena anterior sería calculable con bastante facilidad, ¡y .year
un .year
& .month
!
ACTUALIZACIÓN: He expandido y actualizado significativamente mi respuesta oficial y los detalles de uso del código en mi respuesta a continuación, 100% de respuesta y código de trabajo (en su totalidad), hora / fechas relativas precisas y exactas, sin aproximaciones, gracias.
A continuación, le indicamos cómo agregar algunos métodos de extensión para esto con C # utilizando valores medios:
public static class TimeSpanExtensions
{
public static int GetYears(this TimeSpan timespan)
{
return (int)(timespan.Days/365.2425);
}
public static int GetMonths(this TimeSpan timespan)
{
return (int)(timespan.Days/30.436875);
}
}
Aquí está la respuesta principal con código, tenga en cuenta que puede obtener cualquier número de fechas / tiempos de precisión, segundos y minutos, o segundos, minutos y días, en cualquier lugar hasta años (que contendría 6 partes / segmentos). Si especifica los dos primeros y tiene más de un año, devolverá "1 año y 3 meses atrás" y no devolverá el resto porque solicitó dos segmentos. si solo tiene unas pocas horas, solo volverá "hace 2 horas y 1 minuto". Por supuesto, se aplican las mismas reglas si especifica 1, 2, 3, 4, 5 o 6 segmentos (máximo en 6 porque segundos, minutos, horas, días, meses, años solo hacen 6 tipos). También corregirá problemas de gramáticas como "minutos" versus "minutos" dependiendo de si es de 1 minuto o más, igual para todos los tipos, y la "cadena" generada siempre será gramaticalmente correcta.
Estos son algunos ejemplos de uso: bSellowSegments identifica cuántos segmentos mostrar ... es decir: si es 3, entonces la cadena de retorno sería (como ejemplo) ... "3 years, 2 months and 13 days"
(no incluirá horas, minutos y segundos cuando se devuelven las 3 mejores categorías de tiempo), si la fecha era más reciente, como algo hace unos días, al especificar los mismos segmentos (3) se devolverá "4 days, 1 hour and 13 minutes ago"
cambio, ¡así que todo tiene en cuenta!
si bAllowSegments es 2, devolvería "3 years and 2 months"
y si 6 (valor máximo) devolvería "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"
, pero recuerde que lo hará NEVER RETURN
algo como esto "0 years, 0 months, 0 days, 3 hours, 2 minutes and 13 seconds ago"
ya que entiende que no hay datos de fecha en los 3 primeros segmentos y los ignora, incluso si especifica 6 segmentos, por lo no te preocupes :). Por supuesto, si hay un segmento con 0 en él, lo tendrá en cuenta al formar la cuerda, y se mostrará como "3 days and 4 seconds ago"
e ignorará la parte "0 horas". Disfruta y por favor comenta si quieres.
Public Function RealTimeUntilNow(ByVal dt As DateTime, Optional ByVal bAllowSegments As Byte = 2) As String
'' bAllowSegments identifies how many segments to show... ie: if 3, then return string would be (as an example)...
'' "3 years, 2 months and 13 days" the top 3 time categories are returned, if bAllowSegments is 2 it would return
'' "3 years and 2 months" and if 6 (maximum value) would return "3 years, 2 months, 13 days, 13 hours, 29 minutes and 9 seconds"
Dim rYears, rMonths, rDays, rHours, rMinutes, rSeconds As Int16
Dim dtNow = DateTime.Now
Dim daysInBaseMonth = Date.DaysInMonth(dt.Year, dt.Month)
rYears = dtNow.Year - dt.Year
rMonths = dtNow.Month - dt.Month
If rMonths < 0 Then rMonths += 12 : rYears -= 1 '' add 1 year to months, and remove 1 year from years.
rDays = dtNow.Day - dt.Day
If rDays < 0 Then rDays += daysInBaseMonth : rMonths -= 1
rHours = dtNow.Hour - dt.Hour
If rHours < 0 Then rHours += 24 : rDays -= 1
rMinutes = dtNow.Minute - dt.Minute
If rMinutes < 0 Then rMinutes += 60 : rHours -= 1
rSeconds = dtNow.Second - dt.Second
If rSeconds < 0 Then rSeconds += 60 : rMinutes -= 1
'' this is the display functionality
Dim sb As StringBuilder = New StringBuilder()
Dim iSegmentsAdded As Int16 = 0
If rYears > 0 Then sb.Append(rYears) : sb.Append(" year" & If(rYears <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
If rMonths > 0 Then sb.AppendFormat(rMonths) : sb.Append(" month" & If(rMonths <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
If rDays > 0 Then sb.Append(rDays) : sb.Append(" day" & If(rDays <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
If rHours > 0 Then sb.Append(rHours) : sb.Append(" hour" & If(rHours <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
If rMinutes > 0 Then sb.Append(rMinutes) : sb.Append(" minute" & If(rMinutes <> 1, "s", "") & ", ") : iSegmentsAdded += 1
If bAllowSegments = iSegmentsAdded Then GoTo parseAndReturn
If rSeconds > 0 Then sb.Append(rSeconds) : sb.Append(" second" & If(rSeconds <> 1, "s", "") & "") : iSegmentsAdded += 1
parseAndReturn:
'' if the string is entirely empty, that means it was just posted so its less than a second ago, and an empty string getting passed will cause an error
'' so we construct our own meaningful string which will still fit into the "Posted * ago " syntax...
If sb.ToString = "" Then sb.Append("less than 1 second")
Return ReplaceLast(sb.ToString.TrimEnd(" ", ",").ToString, ",", " and")
End Function
Por supuesto, necesitará una función "ReemplazarÚltima", que toma una cadena de origen, y un argumento que especifica lo que necesita ser reemplazado, y otra arg que especifica con qué desea reemplazarla, y solo reemplaza la última ocurrencia de esa cadena ... he incluido el mío si no tienes uno o no quieres implementarlo, así que aquí está, funcionará "tal cual" sin necesidad de modificaciones. Sé que la función reverseit ya no es necesaria (existe en .net) pero los funcionamientos ReplaceLast y ReverseIt se transfieren de los días de pre.net, así que disculpe qué tan anticuada puede parecer (todavía funciona al 100%, ha estado usando em por más de diez años, puede garantizar que están libres de errores) ... :). Además, si está utilizando VB6, puede usar StrReverse (envolviéndolo alrededor de la cadena extendida con el método de extensión .ReverseIt), en lugar de usar la función ReverseIt () (proporcionada como método de extensión). Entonces, en lugar de hacer sReplacable.ReverseIt, harías StrReverse (sReplacable) ya que StrReverse () es una función incorporada de VB6 (y hace exactamente lo mismo, invierte una cadena dada, y no hace nada más). Si usa StrReverse () en lugar de mi función ReverseIt genérica, puede borrar la función / extensión ReverseIt. La función StrReverse () debe estar disponible en .NET siempre que importe la biblioteca heredada ms-visualbasic-dll. No importa de ninguna manera, escribí ReverseIt () incluso antes de saber que había existido una función StrReverse (), y que la había estado usando por costumbre (no había una razón real para usar la mía en lugar de la función genérica incorporada) StrReverse) - de hecho, estoy seguro de que StrReverse (o una versión específica .NET más nueva y similar de una función de inversión de cadenas) se escribiría para ser más eficiente :). aclamaciones.
<Extension()> _
Public Function ReplaceLast(ByVal sReplacable As String, ByVal sReplaceWhat As String, ByVal sReplaceWith As String) As String
'' let empty string arguments run, incase we dont know if we are sending and empty string or not.
sReplacable = sReplacable.ReverseIt
sReplacable = Replace(sReplacable, sReplaceWhat.ReverseIt, sReplaceWith.ReverseIt, , 1) '' only does first item on reversed version!
Return sReplacable.ReverseIt.ToString
End Function
<Extension()> _
Public Function ReverseIt(ByVal strS As String, Optional ByVal n As Integer = -1) As String
Dim strTempX As String = "", intI As Integer
If n > strS.Length Or n = -1 Then n = strS.Length
For intI = n To 1 Step -1
strTempX = strTempX + Mid(strS, intI, 1)
Next intI
ReverseIt = strTempX + Right(strS, Len(strS) - n)
End Function
Bueno, mejor tarde, entonces nada, supongo;)
Y esta es mi versión modificada:
private string GetElapsedTime(DateTime from_date, DateTime to_date) {
int years;
int months;
int days;
int hours;
int minutes;
int seconds;
int milliseconds;
//------------------
// Handle the years.
//------------------
years = to_date.Year - from_date.Year;
//------------------------
// See if we went too far.
//------------------------
DateTime test_date = from_date.AddMonths(12 * years);
if (test_date > to_date)
{
years--;
test_date = from_date.AddMonths(12 * years);
}
//--------------------------------
// Add months until we go too far.
//--------------------------------
months = 0;
while (test_date <= to_date)
{
months++;
test_date = from_date.AddMonths(12 * years + months);
}
months--;
//------------------------------------------------------------------
// Subtract to see how many more days, hours, minutes, etc. we need.
//------------------------------------------------------------------
from_date = from_date.AddMonths(12 * years + months);
TimeSpan remainder = to_date - from_date;
days = remainder.Days;
hours = remainder.Hours;
minutes = remainder.Minutes;
seconds = remainder.Seconds;
milliseconds = remainder.Milliseconds;
return (years > 0 ? years.ToString() + " years " : "") +
(months > 0 ? months.ToString() + " months " : "") +
(days > 0 ? days.ToString() + " days " : "") +
(hours > 0 ? hours.ToString() + " hours " : "") +
(minutes > 0 ? minutes.ToString() + " minutes " : "");}
Con .Net 4.5 y la clase CultureInfo
, se pueden agregar meses y años a una fecha determinada.
DateTime datetime = DateTime.UtcNow;
int years = 15;
int months = 7;
DateTime yearsAgo = CultureInfo.InvariantCulture.Calendar.AddYears(datetime, -years);
DateTime monthsInFuture = CultureInfo.InvariantCulture.Calendar.AddMonths(datetime, months);
Como eso es mucho tipeo, prefiero crear métodos de extensión:
public static DateTime AddYears(this DateTime datetime, int years)
{
return CultureInfo.InvariantCulture.Calendar.AddYears(datetime, years);
}
public static DateTime AddMonths(this DateTime datetime, int months)
{
return CultureInfo.InvariantCulture.Calendar.AddMonths(datetime, months);
}
DateTime yearsAgo = datetime.AddYears(-years);
DateTime monthsInFuture = datetime.AddMonths(months);
Creo que el siguiente método es bastante confiable y sencillo, ya que se basa en el cálculo de la fecha del marco y devuelve cadenas de tiempo transcurrido legibles como las de Facebook. Perdón por las pequeñas palabras en portugués y el tratamiento plural, en mi caso fue necesario.
public static string ElapsedTime(DateTime dtEvent)
{
TimeSpan TS = DateTime.Now - dtEvent;
int intYears = TS.Days / 365;
int intMonths = TS.Days / 30;
int intDays = TS.Days;
int intHours = TS.Hours;
int intMinutes = TS.Minutes;
int intSeconds = TS.Seconds;
if (intYears > 0) return String.Format("há {0} {1}", intYears, (intYears == 1) ? "ano" : "anos");
else if (intMonths > 0) return String.Format("há {0} {1}", intMonths, (intMonths == 1) ? "mês" : "meses");
else if (intDays > 0) return String.Format("há {0} {1}", intDays, (intDays == 1) ? "dia" : "dias");
else if (intHours > 0) return String.Format("há ± {0} {1}", intHours, (intHours == 1) ? "hora" : "horas");
else if (intMinutes > 0) return String.Format("há ± {0} {1}", intMinutes, (intMinutes == 1) ? "minuto" : "minutos");
else if (intSeconds > 0) return String.Format("há ± {0} {1}", intSeconds, (intSeconds == 1) ? "segundo" : "segundos");
else
{
return String.Format("em {0} às {1}", dtEvent.ToShortDateString(), dtEvent.ToShortTimeString());
}
}
Diría que el TimeSpan actual es un objeto de tiempo real, es decir, la cantidad de tiempo entre el 1 de enero de 2008, 1:31 a.m. y el 3 de febrero de 2008 a las 6:45 a.m., es la misma cantidad de tiempo entre el 5 de febrero y el 5 de febrero. , 2008 a la 1:45 p.m. y el 9 de marzo a las 6:59 p.m. Lo que estás buscando es en realidad la diferencia entre dos fechas.
En cuanto a .MakeMagicHappen.gimmeSomethingPretty.surelyMShasThoughtAboutThisDilema para satisfacer las necesidades específicas de su sistema, es por eso que la gente lo contrata como programador. Si el marco que está utilizando hace absolutamente todo, su empresa solo podría presionar un botón y su sistema saldría completamente formado y usted estaría en la línea de desempleo junto con el resto de los programadores.
Lo que estás buscando no es lo que TimeSpan
representa. TimeSpan
representa un intervalo como un conteo de tics, sin respeto a un DateTime
o Calendar
base.
Un nuevo tipo DateDifference
podría tener más sentido aquí, con un constructor o método de fábrica que tome una base DateTime
, un DateTime
objetivo y, opcionalmente, un Calendar
(predeterminado en CultureInfo.CurrentCulture) con el que calcular los diversos componentes de diferencia (años, meses, etc. .)
EDITAR: me parece que Noda Time puede tener las herramientas que necesita para esto: la clase Period
"[r] epresenta un período de tiempo expresado en términos cronológicos humanos: horas, días, semanas, meses, etc.", y en en particular, Period.Between(then, now, PeriodUnits.AllUnits)
parece ser el cálculo preciso que está solicitando, pero necesariamente es una clase mucho más compleja que TimeSpan
. La página de Conceptos clave en el wiki de Noda Time explica cómo "los humanos hacen el tiempo desordenado":
Dejando de lado los trozos difíciles de la astronomía y la relatividad, la humanidad todavía se ha tomado el tiempo difícil para negociar. Si todos usáramos tics de la época de Unix para hablar sobre el tiempo, no habría necesidad de una biblioteca como Noda Time.
Pero no, nos gusta hablar en años, meses, días, semanas, y por alguna razón nos gustan las 12 p.m. (que confusamente llega antes de la 1 p.m.) para ser aproximadamente la hora en que el sol está más alto ... así que tenemos zonas horarias .
No solo eso, sino que no todos estamos de acuerdo en cuántos meses hay. Diferentes civilizaciones han presentado diferentes formas de dividir el año y diferentes números para los años. Estos son sistemas de calendario .