7

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

Matt
  • 4,309
  • 7
  • 38
  • 52
  • I'm not sure I understand the question - how is this failing? What is "timestamp_received"? The only reason I can imagine this failing is if the timestamp is a timestamp in *local time*. – Paul Jan 12 '17 at 13:57
  • It's a timestamp extracted from a French website which is based on the local timezone(Europe/Paris). When I executed this function, the return datetime has a shift of one hour if I executed it on my laptop or by a Lambda function. I'm suppose the problem is that "datetime.datetime.fromtimestamp" is based on the local timezone... – Matt Jan 12 '17 at 14:03

1 Answers1

6

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.

Paul
  • 10,381
  • 13
  • 48
  • 86
  • Thanks a lot Paul, it works with the first solution! Still really confusing for me the datetime/timestamp manipulation with different timezone :( – Matt Jan 12 '17 at 14:33
  • @Matt I updated my answer, hopefully that clarifies what's going on. – Paul Jan 12 '17 at 14:52
  • thanks alot for your explanation! It's more clear now for me. – Matt Jan 13 '17 at 16:26