24

I have a javascript calendar that is sending me a unixtimestamp. I am in Singapore. I want this timestamp to be interpreted as a Singapore timestamp and then converted to utc for comparisons with the db.

I cant, for the life of myself, figure out how to tell django that this time stamp is from the current timezone, Singapore.

When i do a print statement of the timestamp, it adds 8 hours to the time (which means that django thinks I input the time in utc and is localizing it to the Singaporean context)

Among many other things, I tried: start=datetime.datetime.fromtimestamp(int(start_date)).replace(tzinfo=get_current_timezone())

The start_date is 1325376000 (which translates to 2012-01-01 00:00:00)

However,when i print the output of this I get 2012-01-01 08:00:00+06:55. I dont even know where +06:55 is coming from when singapore is +08:00. I am SO lost.

Thanks for your help.

settings.py:

TIME_ZONE = 'Asia/Singapore'

USE_TZ = True

nknj
  • 2,436
  • 5
  • 31
  • 45
  • posix timestamp `1325376000` is `'2012-01-01 00:00:00 UTC+0000'` and `'2012-01-01 08:00:00 SGT+0800'`. Why do you think it should be `00:00:00` in Singapore? – jfs Oct 03 '12 at 17:29

3 Answers3

36

all methods above are valide, but not "django like". Here is a simple example, how a django programmer would do that:

from datetime import datetime

from django.utils.timezone import make_aware


# valid timestamp
value = 1531489250 
# you can pass the following obj to a DateTimeField, when your settings.USE_TZ == True
datetime_obj_with_tz = make_aware(datetime.fromtimestamp(value))

See more utilites on the Django github timezone module to get whole overview...

AntonTitov
  • 361
  • 3
  • 7
  • 3
    note: this solution introduces an unnecessary conversion via naive local time (returned by `fromtimestamp()` if `tz` argument is not passed) that may fail in various ways e.g., on systems where `fromtimestamp()` has no access to the tz database or for ambiguous local times. [Pass the tz argument explicitly as shown in my answer](https://stackoverflow.com/a/32163867/4279) and you don't need to call `make_aware()` in this case (passing `tz` to `fromtimestamp()` leads to `tz.fromutc()` call that is more robust than `tz.localize()` called by `make_aware()`). – jfs Sep 10 '19 at 13:48
27

Assuming you've got pytz installed:

from datetime import datetime
import pytz
local_tz = pytz.timezone("Asia/Singapore") 
utc_dt = datetime.utcfromtimestamp(timestamp).replace(tzinfo=pytz.utc)
local_dt = local_tz.normalize(utc_dt.astimezone(local_tz))

For example:

>>> from datetime import datetime
>>> import pytz
>>> local_tz = pytz.timezone("Asia/Singapore")
>>> utc_dt = datetime.utcfromtimestamp(1325376000).replace(tzinfo=pytz.utc)
>>> utc_dt
datetime.datetime(2012, 1, 1, 0, 0, tzinfo=<UTC>)
>>> local_dt = local_tz.normalize(utc_dt.astimezone(local_tz))
>>> local_dt
datetime.datetime(2012, 1, 1, 8, 0, tzinfo=<DstTzInfo 'Asia/Singapore' SGT+8:00:00 STD>)
>>> local_dt.replace(tzinfo=None)
datetime.datetime(2012, 1, 1, 8, 0)
David Wolever
  • 148,955
  • 89
  • 346
  • 502
  • What part of my answer depends on the server's timezone? Also, `pytz`'s `tz.localize(dt)` is identical (in this case) to `tz.normalize(dt.replace(tzinfo=pytz.utc).astimezone(tz))`: http://pytz.sourceforge.net/#localized-times-and-date-arithmetic – David Wolever Oct 03 '12 at 18:41
  • 3
    FFUUU! Ahh, thank you! You're right. I'm an idiot. My answer has been updated to give the actual, correct answer. – David Wolever Oct 04 '12 at 23:21
  • "Assuming you've got `pytz` installed" is too much... :( – Jorge Leitao Jun 21 '14 at 06:58
  • Why is that? pytz is an easy install (or copy+paste, if you prefer). If you're only dealing with one time zone *and* that timezone doesn't have daylight savings *and* the UTC offset hasn't changed (and won't change) in the timeframe your application will be dealing with, then it wouldn't be too hard to write your own timezone handling… but otherwise it will be almost guaranteed to be incorrect. – David Wolever Jun 21 '14 at 07:03
  • It works well, the only thing is I have to insert the timezone manually, how can I make this dynamic so it works for users from everywhere? Where do I get the timezone from? – Jesus Almaral - Hackaprende Aug 09 '19 at 16:00
  • @JesusAlmaral-Hackaprende: the question mentions Django-specific: `timezone.get_current_timezone()` – jfs Jul 26 '20 at 07:40
7

Pass the pytz tzinfo object to fromtimestamp() method:

#!/usr/bin/env python
from datetime import datetime
import pytz # $ pip install pytz

tz = pytz.timezone("Asia/Singapore")
print(datetime.fromtimestamp(1325376000, tz))
# -> 2012-01-01 08:00:00+08:00

Note: the result object is timezone-aware: you could compare it with other aware datetime objects i.e., you don't need to convert it to UTC for comparison -- you can use it as is.

I dont even know where +06:55 is coming from when singapore is +08:00.

You see +06:55 due to the invalid .replace() call. get_current_timezone() returns pytz.timezone("Asia/Singapore") that has a variable utc offset (it may have a different utc offset at different dates). When you call .replace() some random (depends on the implementation) tzinfo object is used. The issue is that .replace() method does not allow pytz.timezone("Asia/Singapore") to choose the correct tzinfo for the input date.

>>> list(tz._tzinfos.values())
[<DstTzInfo 'Asia/Singapore' MALT+7:00:00 STD>,
 <DstTzInfo 'Asia/Singapore' MALT+7:20:00 STD>,
 <DstTzInfo 'Asia/Singapore' JST+9:00:00 STD>,
 <DstTzInfo 'Asia/Singapore' SMT+6:55:00 STD>,
 <DstTzInfo 'Asia/Singapore' SGT+7:30:00 STD>,
 <DstTzInfo 'Asia/Singapore' MALT+7:30:00 STD>,
 <DstTzInfo 'Asia/Singapore' MALST+7:20:00 DST>,
 <DstTzInfo 'Asia/Singapore' LMT+6:55:00 STD>,
 <DstTzInfo 'Asia/Singapore' SGT+8:00:00 STD>]

i.e., both +06:55 and +0800 are valid (at different dates) for Singapore. That is why you should use .replace() only with timezones that have a constant utc offset such as the utc timezone itself (the offset is zero, always for any date).

fromtimestamp(,tz) method calls tz.fromutc() internally that allows tz to choose the correct offset for a given utc time.

jfs
  • 399,953
  • 195
  • 994
  • 1,670