programming lenguaje google golang developer go time duration

lenguaje - Tiempo de golang. Desde() con meses y años.



google''s go (5)

Estoy tratando de convertir una marca de tiempo como esta:

2015-06-27T09:34:22+00:00

a un tiempo desde el formato por lo que diría que hace 9 meses 1 día 2 horas 30 minutos 2 segundos.

algo como eso.

Utilicé time.Parse y time.Since para llegar a esto:

6915h7m47.6901559s

Pero, ¿cómo me convierto a partir de ahí? Algo como esto es lo que pensé:

for hours > 24 { days++ hours -= 24 }

Pero el problema con esto es que no será preciso durante meses porque los meses pueden tener 28, 30 y 31 días.

¿Hay una mejor manera de lograr lo que quiero?


Algo como esto funcionaría, probablemente no sea el más eficiente, pero es tan preciso como lo vas a obtener:

func main() { a := time.Date(2015, 10, 15, 0, 0, 0, 0, time.UTC) b := time.Date(2016, 11, 15, 0, 0, 0, 0, time.UTC) fmt.Println(monthYearDiff(a, b)) } func monthYearDiff(a, b time.Time) (years, months int) { m := a.Month() for a.Before(b) { a = a.Add(time.Hour * 24) m2 := a.Month() if m2 != m { months++ } m = m2 } years = months / 12 months = months % 12 return }

playground


La solución propuesta por izca es genial, pero se pierde una cosa. Si agrega el siguiente ejemplo, puede ver el efecto:

a = time.Date(2015, 1, 11, 0, 0, 0, 0, time.UTC) b = time.Date(2015, 3, 10, 0, 0, 0, 0, time.UTC) fmt.Println(diff(a, b)) // Expected: 0 1 27 0 0 0 // Actual output: 0 1 30 0 0 0

playground

El código calcula los días restantes del siguiente mes incompleto basándose en los días totales del primer mes ( y1,M1 ), pero debe calcularse a partir del mes anterior del mes de la fecha posterior ( y2,M2-1 ).

El código final es el siguiente:

package main import ( "fmt" "time" ) func DaysIn(year int, month time.Month) int { return time.Date(year, month+1, 0, 0, 0, 0, 0, time.UTC).Day() } func Elapsed(from, to time.Time) (inverted bool, years, months, days, hours, minutes, seconds, nanoseconds int) { if from.Location() != to.Location() { to = to.In(to.Location()) } inverted = false if from.After(to) { inverted = true from, to = to, from } y1, M1, d1 := from.Date() y2, M2, d2 := to.Date() h1, m1, s1 := from.Clock() h2, m2, s2 := to.Clock() ns1, ns2 := from.Nanosecond(), to.Nanosecond() years = y2 - y1 months = int(M2 - M1) days = d2 - d1 hours = h2 - h1 minutes = m2 - m1 seconds = s2 - s1 nanoseconds = ns2 - ns1 if nanoseconds < 0 { nanoseconds += 1e9 seconds-- } if seconds < 0 { seconds += 60 minutes-- } if minutes < 0 { minutes += 60 hours-- } if hours < 0 { hours += 24 days-- } if days < 0 { days += DaysIn(y2, M2-1) months-- } if months < 0 { months += 12 years-- } return } func main() { var a, b time.Time a = time.Date(2015, 5, 1, 0, 0, 0, 0, time.UTC) b = time.Date(2016, 6, 2, 1, 1, 1, 1, time.UTC) fmt.Println(Elapsed(a, b)) // Expected: false 1 1 1 1 1 1 a = time.Date(2016, 1, 2, 0, 0, 0, 0, time.UTC) b = time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC) fmt.Println(Elapsed(a, b)) // Expected: false 0 0 30 0 0 0 a = time.Date(2016, 2, 2, 0, 0, 0, 0, time.UTC) b = time.Date(2016, 3, 1, 0, 0, 0, 0, time.UTC) fmt.Println(Elapsed(a, b)) // Expected: false 0 0 28 0 0 0 a = time.Date(2015, 2, 11, 0, 0, 0, 0, time.UTC) b = time.Date(2016, 1, 12, 0, 0, 0, 0, time.UTC) fmt.Println(Elapsed(a, b)) // Expected: false 0 11 1 0 0 0 a = time.Date(2015, 1, 11, 0, 0, 0, 0, time.UTC) b = time.Date(2015, 3, 10, 0, 0, 0, 0, time.UTC) fmt.Println(Elapsed(a, b)) // Expected: false 0 1 27 0 0 0 }

playground


Los días en un mes dependen de la fecha, al igual que los días en un año (años bisiestos).

Si usa time.Since() para obtener el tiempo transcurrido desde un valor time.Time , o cuando calcula la diferencia entre 2 valores time.Time usando el método Time.Sub() , el resultado es un time.Duration que pierde el contexto de tiempo (como Duration es solo la diferencia de tiempo en nanosegundos). Esto significa que no puede calcular con precisión y sin ambigüedad la diferencia en años, meses, etc. a partir de un valor de Duration .

La solución correcta debe calcular la diferencia en el contexto del tiempo. Puede calcular la diferencia para cada campo (año, mes, día, hora, minuto, segundo) y luego normalizar el resultado para que no tenga ningún valor negativo. También se recomienda intercambiar los valores de Time si la relación entre ellos no es la esperada.

La normalización significa que si un valor es negativo, agregue el valor máximo de ese campo y disminuya el siguiente campo en 1. Por ejemplo, si los seconds son negativos, agregue 60 y disminuya los minutes en 1. Una cosa a tener en cuenta es cuando se normaliza Diferencia de días (días en mes), el número de días en el mes apropiado debe ser aplicado. Esto se puede calcular fácilmente con este pequeño truco:

// Max days in year y1, month M1 t := time.Date(y1, M1, 32, 0, 0, 0, 0, time.UTC) daysInMonth := 32 - t.Day()

La lógica detrás de esto es que el día 32 es más grande que el día máximo en cualquier mes. Se normalizará automáticamente (los días adicionales se transfieren al mes y el día se reducen adecuadamente). Y cuando restamos el día que tenemos después de la normalización de 32, obtenemos exactamente lo que fue el último día del mes.

Manejo de zona horaria:

El cálculo de la diferencia solo dará un resultado correcto si los dos valores de tiempo que pasamos están en la misma zona horaria ( time.Location ). Incorporamos un control a nuestra función: si este no es el caso, "convertimos" uno de los valores de tiempo para que esté en la misma ubicación que el otro utilizando el método Time.In() :

if a.Location() != b.Location() { b = b.In(a.Location()) }

Aquí hay una solución que calcula la diferencia en año, mes, día, hora, min, seg:

func diff(a, b time.Time) (year, month, day, hour, min, sec int) { if a.Location() != b.Location() { b = b.In(a.Location()) } if a.After(b) { a, b = b, a } y1, M1, d1 := a.Date() y2, M2, d2 := b.Date() h1, m1, s1 := a.Clock() h2, m2, s2 := b.Clock() year = int(y2 - y1) month = int(M2 - M1) day = int(d2 - d1) hour = int(h2 - h1) min = int(m2 - m1) sec = int(s2 - s1) // Normalize negative values if sec < 0 { sec += 60 min-- } if min < 0 { min += 60 hour-- } if hour < 0 { hour += 24 day-- } if day < 0 { // days in month: t := time.Date(y1, M1, 32, 0, 0, 0, 0, time.UTC) day += 32 - t.Day() month-- } if month < 0 { month += 12 year-- } return }

Algunas pruebas:

var a, b time.Time a = time.Date(2015, 5, 1, 0, 0, 0, 0, time.UTC) b = time.Date(2016, 6, 2, 1, 1, 1, 1, time.UTC) fmt.Println(diff(a, b)) // Expected: 1 1 1 1 1 1 a = time.Date(2016, 1, 2, 0, 0, 0, 0, time.UTC) b = time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC) fmt.Println(diff(a, b)) // Expected: 0 0 30 0 0 0 a = time.Date(2016, 2, 2, 0, 0, 0, 0, time.UTC) b = time.Date(2016, 3, 1, 0, 0, 0, 0, time.UTC) fmt.Println(diff(a, b)) // Expected: 0 0 28 0 0 0 a = time.Date(2015, 2, 11, 0, 0, 0, 0, time.UTC) b = time.Date(2016, 1, 12, 0, 0, 0, 0, time.UTC) fmt.Println(diff(a, b)) // Expected: 0 11 1 0 0 0

La salida es la esperada:

1 1 1 1 1 1 0 0 30 0 0 0 0 0 28 0 0 0 0 11 1 0 0 0

Pruébalo en el Go Playground .

Para calcular cuántos años tienes:

// Your birthday: let''s say it''s January 2nd, 1980, 3:30 AM birthday := time.Date(1980, 1, 2, 3, 30, 0, 0, time.UTC) year, month, day, hour, min, sec := diff(birthday, time.Now()) fmt.Printf("You are %d years, %d months, %d days, %d hours, %d mins and %d seconds old.", year, month, day, hour, min, sec)

Ejemplo de salida:

You are 36 years, 3 months, 8 days, 11 hours, 57 mins and 41 seconds old.

La fecha / hora mágica en la que comienza la hora de ir al patio de juegos es: 2009-11-10 23:00:00 UTC
Este es el momento en que Go fue anunciado por primera vez. Vamos a calcular qué edad tiene Go:

goAnnounced := time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC) year, month, day, hour, min, sec := diff(goAnnounced, time.Now()) fmt.Printf("Go was announced "+ "%d years, %d months, %d days, %d hours, %d mins and %d seconds ago.", year, month, day, hour, min, sec)

Salida:

Go was announced 6 years, 4 months, 29 days, 16 hours, 53 mins and 31 seconds ago.


Puede intentar trabajar con mi paquete de date , que incluye el paquete de period para trabajar con períodos de tiempo de estilo ISO ( Wikipedia ).

El tipo Period viene con un formateador que entiende plurales, imprimiendo cadenas legibles como "9 años, 2 meses" y "3 horas, 4 minutos, 1 segundo", junto con los equivalentes ISO ("P9Y2M" y "PT3H4M1S").

Los períodos son, por supuesto, complicados debido a la duración variable de días (debido a DST) y meses (debido al calendario gregoriano). El paquete de period intenta ayudarlo proporcionando una API que permite cálculos precisos e imprecisos. Para períodos cortos (hasta ± 3276 horas) puede convertir una Duración de forma precisa.

duration := time.Since(...) p, _ := period.NewOf(duration) str := p.String()

Si necesita duraciones precisas en períodos más largos, debe utilizar la función Entre (que representa la excelente respuesta de icza).

p := period.Between(t1, t2) str := p.String()


Si usa PostgreSQL, puede obtener fácilmente el resultado con la función age .

Supongamos que tienes dos fechas b .

Como dijo icza, ten cuidado, b deben estar en la misma zona horaria.

Primero, puede invocar age con dos parámetros, en su caso, fecha a y fecha b . Esta función devuelve un tipo de intervalo que contiene años, meses, semanas, días, horas, minutos, segundos y milisegundos.

SELECT age(''2016-03-31'', ''2016-06-30''); -- result is: -2 mons -30 days

La segunda posibilidad es usar la función de age con un parámetro. El resultado también es un intervalo, pero en este caso, se resta la age de current_date (a medianoche). Supongamos que hoy es 16/06/2016:

SELECT age(timestamp ''2016-06-30''); -- result is: -14 days

Tenga en cuenta que la palabra clave de timestamp es necesaria para emitir la fecha ''2016-06-30''.

Para obtener más información, puede usar date_part o directamente la función de extract que devuelve un campo específico (años, meses, días ...).

SELECT date_part(''month'', age(''2016-03-31'', ''2016-06-30'')); --result is: -2 SELECT date_part(''day'', age(''2016-03-31'', ''2016-06-30'')); --result is: -30

Solicitud completa:

SELECT date_part(''year'', diff) as year , date_part(''month'', diff) as month , date_part(''day'', diff) as day FROM ( SELECT age(timestamp ''2016-06-30'') AS diff ) as qdiff; -- result is: -- year month day -- 0 0 -14

(con CTE - Expresión de tabla común):

WITH qdiff AS ( SELECT age(timestamp ''2016-06-30'') AS diff ) SELECT date_part(''year'', diff) as year , date_part(''month'', diff) as month , date_part(''day'', diff) as day FROM qdiff -- result is: -- year month day -- 0 0 -14

Documentación de PostgreSQL (versión actual): https://www.postgresql.org/docs/current/static/functions-datetime.html