getmtime () vs datetime.now ():

8

Este código imprime um aviso falso uma vez por ano, na noite do relógio (horário de verão da Europa Central para horário da Europa Central):

import os
import datetime

now = datetime.datetime.now()
age = now - datetime.datetime.fromtimestamp(os.path.getmtime(file_name))
if (age.seconds + age.days * 24 * 3600) < -180:
    print('WARN: file has timestap from future?: %s' % age)

Como fazer esse código funcionar mesmo durante esse turno anual de uma hora?

Atualizar

Eu me importo apenas com a idade, não com a data e hora.

guettli
fonte

Respostas:

6

O fragmento publicado pode ser facilmente aprimorado alternando do horário local para o UTC. Não há alterações de horário de verão no horário de verão. Apenas substitua essas duas funções de data e hora now()-> utcnow()( docs ) e fromtimestamp()-> utcfromtimestamp()( docs ).

No entanto, se a única saída esperada for a duração do arquivo em segundos, podemos usar diretamente os carimbos de data / hora (segundos de uma "época") sem qualquer conversão:

import time
import os.path

...
age = time.time() - os.path.getmtime(file_name)
VPfB
fonte
3
Usar o UTC em primeiro lugar é a abordagem universalmente correta.
Konstantin
@konstantin Por que a abordagem correta? Eu gosto dessa solução simples, pois eu (nesse contexto) ligo apenas para a idade (timedelta) e não para a data e hora.
guettli
@ guettli, eu diria que esta é provavelmente a melhor e mais simples resposta para o seu caso de uso. A coisa mais importante ao comparar o tempo é que você está comparando o mesmo para igual, neste exemplo, é um carimbo de data / hora UTC vs um carimbo de hora UTC, portanto, sempre funcionará. O motivo pelo qual seu código não funcionou originalmente é porque você estava comparando objetos que não tinham mais relevância com um carimbo de data / hora UTC, pois não têm conhecimento de fuso horário. Se você pretende fazer coisas mais complexas, minha resposta pode ser mais útil, pois é mais fácil trabalhar com objetos de data e hora, mas, para uma comparação simples, isso funciona.
KillerKode #
1
@ guettli Eu chamei isso de "abordagem universalmente correta" porque passei muitas horas, se não dias, depurando sistemas e interfaces que só funcionavam com algumas suposições a priori sobre horários e fusos horários recebidos como entrada. Se, por exemplo, seu servidor não for executado no mesmo fuso horário que o cliente e as datas forem transmitidas sem deslocamento explícito do UTC e interpretadas como datas locais, as coisas ainda poderão dar certo (por exemplo, ao calcular deltas), mas é difícil depurar que pode ser facilmente evitado se todos aderissem ao UTC apenas em primeiro lugar / o mais rápido possível.
precisa
1
@ guettli Obrigado por aceitar minha resposta. Espero que tenha sido útil, porque tenho um pouco de medo de que minha resposta curta não valha uma recompensa tão generosa e você me pagou em excesso. Atenciosamente (Schöne Grüße nach Chemnitz)
VPfB 7/19/19
3

seus dois objetos de data e hora são "ingênuos", o que significa que eles não sabem sobre o horário de verão. datetime.now()retorna o horário atual em que sua máquina é executada e isso pode incluir o horário de verão. O mesmo vale para datetime.fromtimestamp(os.path.getmtime()).

# 1 - localizar seus objetos de data e hora pode ser uma opção; algo como

from datetime import datetime
import tzlocal
now_aware = tzlocal.get_localzone().localize(datetime.now())
file_mtime = datetime.fromtimestamp(os.path.getmtime(file))
# assuming the file was created on a machine in the same timezone (!):
file_mtime_aware = now_aware.tzinfo.localize(file_mtime)
age = now_aware - file_mtime_aware

# 2 - outra opção, usando a conversão UTC com datetime:

now = datetime.utcnow()
age = now - datetime.utcfromtimestamp(os.path.getmtime(file_name))
if (age.seconds + age.days * 24 * 3600) < -180:
    print(f'WARN: file has timestamp from future?: {age} s')

# 3 - como VPfB aponta em sua resposta, os.path.getmtimeretorna um carimbo de data / hora UTC (verifique os documentos do módulo e os documentos do módulo de tempo ). Portanto, a solução mais fácil poderia ser ignorar a conversão e, datetimeem primeiro lugar, usar apenas carimbos de data / hora UTC; por exemplo, obter o registro de data e hora UTC atual como time.time().

Trabalhar com fusos horários pode deixá-lo louco ... mas há alguns bons recursos por aí, por exemplo, este post médio .

MrFuppes
fonte
1

Seu problema é que você está conseguindo seu tempo sem que o fuso horário esteja ciente. Portanto, quando os relógios mudam, você termina comparando um carimbo de data / hora anterior ao relógio e outro após o relógio mudar e seu código não vê isso.

Em vez disso, você deve fazer com que seus objetos de data e hora sejam baseados em um fuso horário específico para não ter problemas com a alteração de relógios. Recomendamos o uso do módulo pytz para ajudá-lo. Você pode ver uma lista dos fusos horários disponíveis nesta resposta: Existe uma lista de fusos horários do Pytz?

Aqui está um exemplo de código simples de como você pode fazer isso com objetos com reconhecimento de fuso horário:

import os
from datetime import datetime
import pytz


def get_datetime_now(timezone):
    """
    Returns timezone aware datetime object for right now
    """
    if timezone not in pytz.all_timezones:
        return None
    tz = pytz.timezone(timezone)
    dt = datetime.now().astimezone()
    return dt.astimezone(tz)


def timestamp_to_datetime(timestamp, timezone):
    """
    Returns a datetime object from a timestamp
    """
    if timezone not in pytz.all_timezones:
        return None
    tz = pytz.timezone(timezone)
    dt = datetime.fromtimestamp(timestamp).astimezone()
    return dt.astimezone(tz)


timezone = 'CET'

file_timestamp = os.path.getmtime(file_name)

now = get_datetime_now(timezone)
file_datetime = timestamp_to_datetime(file_timestamp, timezone)
age = now - file_datetime

if (age.seconds + age.days * 24 * 3600) < -180:
    print('WARN: file has timestap from future?: %s' % age)
KillerKode
fonte
Por que sua solução é melhor que age = time.time() - os.path.getmtime(file_name). Estou interessado apenas na idade (delta do tempo) e não na data e hora.
guettli
1
Se você está interessado apenas no delta do tempo, não é. A razão pela qual me aproximei dessa maneira é porque você mencionou que está no fuso horário da CET e mostrou que estava trabalhando com objetos de data e hora, essa abordagem pode ser útil se você estiver comparando tempos entre dois fusos horários diferentes. Se o seu fuso horário for o mesmo, basta comparar os carimbos de data e hora. A única outra consideração é garantir que a hora do sistema esteja sincronizada com um servidor NTP.
precisa saber é o seguinte