1

I want to save the datetime in the database as midnight for a specific user. I save the timezone on my user object. However, the database keeps saving it as midnight in UTC. Below is the code I am using.

tz_obj = pytz.timezone(user.timezone)
start_date = tz_obj.normalize(date_obj.replace(day=1, hour=0, minute=0, second=0, microsecond=0))
end_date = tz_obj.normalize(date_obj.replace(day=calendar.monthrange(date_obj.year, date_obj.month)[1], hour=23, minute=59,
                                    second=59, microsecond=99999))
obj = MyObject.objects.create(start=start_date, end=end_date)

Can someone show me how to make sure that the UTC date saved in the database is equivalent to midnight in the specified timezone.

Update Each user could have a different timezone so setting the timezone in the settings file does not solve this problem.

jfs
  • 399,953
  • 195
  • 994
  • 1,670
Dan
  • 3,777
  • 4
  • 25
  • 23
  • @DhiaTN: Each user could have different timezones. The answer you provided does not help. I did not mean to +1 your comment. – Dan Dec 15 '15 at 14:51
  • Your question was not clear enough, it's great that you updated it :) – Dhia Dec 15 '15 at 14:54
  • Sorry. Thought I did with showing that the timezone is saved on the user object. – Dan Dec 15 '15 at 14:55
  • all three answers here are wrong. It is ok. The accepted answer to the duplicate has been wrong for several years too (now it is less wrong -- it works in more cases). Use my answer there if you need a correct result (according to the tz database) or a loud failure when the code can't get the result. – jfs Dec 17 '15 at 15:26

2 Answers2

1

Note: As in comments, this approach is not correct. We need to take date object in user's timezone, as in the answer in duplicate question.

So, instead of date.today(), we would need: datetime.now(tz).date().


If I get your question right, you want to store midnight in suppose timezone +5:30, as 18:30 in UTC? If that is the case, you can do it like this:

>>> from datetime import date, time, datetime
>>> naive_midnight = datetime.combine(date.today(), time())
>>> tz = pytz.timezone('Asia/Kolkata')
>>>
>>> # Attach local timezone to above naive datetime object
>>> midnight_user_timezone = tz.localize(naive_midnight)
>>> print midnigth_user_timezone
2015-12-15 00:00:00+05:30
>>>
>>> # Convert to UTC
>>> midnight_to_utc = midnight_user_timezone.astimezone(pytz.UTC)
>>> print midnigth_to_utc
2015-12-14 18:30:00+00:00
Rohit Jain
  • 209,639
  • 45
  • 409
  • 525
  • This is working. The last step is not necessary since Django saves to UTC by default. – Dan Dec 15 '15 at 23:31
  • @Dan: it is wrong and it can't work unless the local timezone for your server is Asia/Kolkata. Your server's timezone should be irrelevant and it can't be equal to `user.timezone` in all cases (users may use different timezones). – jfs Dec 16 '15 at 21:01
  • @J.F.Sebastian I don't understand. Can you please explain what's wrong with the approach? That would be great :) – Rohit Jain Dec 17 '15 at 15:05
  • do you understand that the local date (`date.today()`) may be different from the date in `user.timezone` (`datetime.now(tz).date()`)? Read my answer to the duplicate question – jfs Dec 17 '15 at 15:06
  • @J.F.Sebastian Oh yeah. That might create a 1 day difference. Got it. Thanks :) – Rohit Jain Dec 17 '15 at 15:10
  • note: `date.today()` is not the only issue in your answer e.g., `tz.localize()` may return non-existing time ([there could be a DST transition at midnight](http://stackoverflow.com/questions/12165691/python-datetime-with-timezone-to-epoch/12166400#comment39565270_12166400)) – jfs Dec 18 '15 at 21:20
1

Django has a function named make_aware to convert time zone naive datetime objects to time zone aware datetime objects.

aware_date_obj = make_aware(date_obj, user.timezone)

Since the datetime object is then already linked to the correct time zone, you can simply use your .replace(day=1, hour=0, minute=0, second=0, microsecond=0) on the aware_date_obj and you will get the beginning of the year at the specified time zone.

If the datetime object is already time zone aware but not in the correct time zone, you can use the astimezone to convert it to the correct timezone:

date_obj.astimezone(user.timezone)

Then you can again use the .replace function.

Finally, when Django stores the datetime object in the database, the time zone information will get lost. The time point will still be correct, but it will be linked to the default django time zone after the load from the database. You would then again have to use the astimezone function to link it to the correct time zone.

Tim
  • 1,272
  • 11
  • 28
  • `.replace(day=1,hour=0)` on timezone-aware datetime object may cross a DST boundary (the result may have wrong utc offset). Read my answer to the duplicate question, to understand how to get the midnight correctly. – jfs Dec 17 '15 at 15:05