Search code examples
pythondatetimepytz

Python convert timestamps with specific timezone to datetime in UTC


I'm trying to convert a timestamp with a specific timezone(Europe/Paris) to a datetime format in UTC. From my laptop it works with the solution below but when I'm executing my code in a remote server(AWS- Lambda function in Ireland), I've a shift of 1 hour because the local timezone of the server is different from mine. How can I have a code who can work on my laptop and at the same time in a remote server(dynamically handle local timezone)?

import pytz
import datetime

def convert_timestamp_in_datetime_utc(timestamp_received):
    utc = pytz.timezone('UTC')
    now_in_utc = datetime.datetime.utcnow().replace(tzinfo=utc).astimezone(pytz.UTC)
    fr = pytz.timezone('Europe/Paris')
    new_date = datetime.datetime.fromtimestamp(timestamp_received)
    return fr.localize(new_date, is_dst=None).astimezone(pytz.UTC)

Thanks


Solution

  • I am not sure what timestamp_received is, but I think what you want is utcfromtimestamp()

    import pytz
    from datetime import datetime
    
    def convert_timestamp_in_datetime_utc(timestamp_received):
        dt_naive_utc = datetime.utcfromtimestamp(timestamp_received)
        return dt_naive_utc.replace(tzinfo=pytz.utc)
    

    For completeness, here is another way to accomplish the same thing by referencing python-dateutil's tzlocal time zone:

    from dateutil import tz
    from datetime import datetime
    def convert_timestamp_in_datetime_utc(timestamp_received):
        dt_local = datetime.fromtimestamp(timestamp_received, tz.tzlocal())
    
        if tz.datetime_ambiguous(dt_local):
            raise AmbiguousTimeError
    
        if tz.datetime_imaginary(dt_local):
            raise ImaginaryTimeError
    
        return dt_local.astimezone(tz.tzutc())
    
    
    class AmbiguousTimeError(ValueError):
        pass
    
    class ImaginaryTimeError(ValueError):
        pass
    

    (I added in the AmbiguousTimeError and ImaginaryTimeError conditions to mimic the pytz interface.) Note that I'm including this just in case you have a similar problem that needs to make reference to the local time zone for some reason - if you have something that will give you the right answer in UTC, it's best to use that and then use astimezone to get it into whatever local zone you want it in.

    How it works

    Since you expressed that you were still a bit confused about how this works in the comments, I thought I would clarify why this works. There are two functions that convert timestamps to datetime.datetime objects, datetime.datetime.fromtimestamp(timestamp, tz=None) and datetime.datetime.utcfromtimestamp(timestamp):

    1. utcfromtimestamp(timestamp) will give you a naive datetime that represents the time in UTC. You can then do dt.replace(tzinfo=pytz.utc) (or any other utc implementation - datetime.timezone.utc, dateutil.tz.tzutc(), etc) to get an aware datetime and convert it to whatever time zone you want.

    2. fromtimestamp(timestamp, tz=None), when tz is not None, will give you an aware datetime equivalent to utcfromtimestamp(timestamp).replace(tzinfo=timezone.utc).astimezone(tz). If tz is None, instead of converting too the specified time zone, it converts to your local time (equivalent to dateutil.tz.tzlocal()), and then returns a naive datetime.

    Starting in Python 3.6, you can use datetime.datetime.astimezone(tz=None) on naive datetimes, and the time zone will be assumed to be system local time. So if you're developing a Python >= 3.6 application or library, you can use datetime.fromtimestamp(timestamp).astimezone(whatever_timezone) or datetime.utcfromtimestamp(timestamp).replace(tzinfo=timezone.utc).astimezone(whatever_timezone) as equivalents.