| 468 | .. _time-zones-faq: |
| 469 | |
| 470 | FAQ |
| 471 | === |
| 472 | |
| 473 | Setup |
| 474 | ----- |
| 475 | |
| 476 | 1. **I don't need multiple time zones. Should I enable time zone support?** |
| 477 | |
| 478 | Yes. When time-zone support is enabled, Django uses a more accurate model |
| 479 | of local time. This shields you from subtle and unreproducible bugs around |
| 480 | DST transitions. Remember that your website runs 24/7! |
| 481 | |
| 482 | In this regard, time zones is comparable to ``unicode`` in Python. At first |
| 483 | it's hard. You get encoding and decoding errors. Then you learn the rules. |
| 484 | And some problems disappear -- you never get mangled output again when your |
| 485 | application receives non-ASCII input. |
| 486 | |
| 487 | When you enable time zone support, you'll encounter some errors because |
| 488 | you're using naive datetimes where Django expects aware datetimes. Such |
| 489 | errors show up when running tests and they're easy to fix. You'll quickly |
| 490 | learn how to avoid invalid operations. |
| 491 | |
| 492 | On the other hand, bugs caused by the lack of time zone support are much |
| 493 | harder to prevent, diagnose and fix. Anything that involves scheduled tasks |
| 494 | or datetime arithmetic is a candidate for subtle bugs that will bite you |
| 495 | only once or twice a year. |
| 496 | |
| 497 | For these reasons, time zone support is enabled by default in new projects, |
| 498 | and you should keep it unless you have a very good reason not to. |
| 499 | |
| 500 | 2. **I've enabled time zone support, am I safe?** |
| 501 | |
| 502 | Maybe. You're better protected from DST-related bugs, but you can still |
| 503 | shoot yourself into the foot by carelessly turning naive datetimes into |
| 504 | aware datetimes, and vice-versa. |
| 505 | |
| 506 | If your application connects to other systems, for instance if it queries |
| 507 | a webservice, make sure datetimes are properly specified. To transmit |
| 508 | datetimes safely, their representation should include the UTC offset, or |
| 509 | their values should be in UTC (or both!). |
| 510 | |
| 511 | Finally, our calendar system contains interesting traps for computers:: |
| 512 | |
| 513 | >>> import datetime |
| 514 | >>> def substract_one_year(value): # DON'T DO THAT! |
| 515 | ... return value.replace(year=value.year - 1) |
| 516 | >>> one_year_before(datetime.datetime(2012, 3, 1, 10, 0)) |
| 517 | datetime.datetime(2011, 3, 1, 10, 0) |
| 518 | >>> one_year_before(datetime.datetime(2012, 2, 29, 10, 0)) |
| 519 | Traceback (most recent call last): |
| 520 | ... |
| 521 | ValueError: day is out of range for month |
| 522 | |
| 523 | (To implement this function, you must decide whether 2012-02-29 minus |
| 524 | one year is 2011-02-28 or 2011-03-01, which depends on your business |
| 525 | requirements.) |
| 526 | |
| 527 | 3. **Should I install pytz?** |
| 528 | |
| 529 | Yes. Django has a policy of not requiring external dependencies, and for |
| 530 | this reason pytz_ is optional. However, it's much safer to install it. |
| 531 | |
| 532 | As soon as you activate time zone support, Django needs a definition of the |
| 533 | default time zone. When pytz is available, Django loads this definition |
| 534 | from the `tz database`_. This is the most accurate solution. Otherwise, it |
| 535 | relies on the difference between local time and UTC, as reported by the |
| 536 | operating system, to compute conversions. This is less reliable, especially |
| 537 | around DST transitions. |
| 538 | |
| 539 | Furthermore, if you want to support users in more than one time zone, pytz |
| 540 | is the reference for time zone definitions. |
| 541 | |
| 542 | Troubleshooting |
| 543 | --------------- |
| 544 | |
| 545 | 1. **My application crashes with** ``TypeError: can't compare offset-naive`` |
| 546 | ``and offset-aware datetimes`` **-- what's wrong?** |
| 547 | |
| 548 | First, don't panic. Then, let's reproduce this error, simply by comparing a |
| 549 | naive and an aware datetime:: |
| 550 | |
| 551 | >>> import datetime |
| 552 | >>> from django.utils import timezone |
| 553 | >>> datetime.datetime.utcnow().replace(tzinfo=timezone.utc) == datetime.datetime.utcnow() |
| 554 | Traceback (most recent call last): |
| 555 | ... |
| 556 | TypeError: can't compare offset-naive and offset-aware datetimes |
| 557 | |
| 558 | If you encounter this error, most likely, your code is comparing: |
| 559 | |
| 560 | - a datetime provided by Django, for instance a value read from a form or |
| 561 | a model field: since you enabled time zone support, it is aware; |
| 562 | - a datetime generated by your code, which is naive (or you wouldn't be |
| 563 | reading this). |
| 564 | |
| 565 | Generally, the correct solution is to change your code to use an aware |
| 566 | datetime instead. |
| 567 | |
| 568 | If you're writing a pluggable application that's expected to work |
| 569 | independently of the value of :setting:`USE_TZ`, you may find |
| 570 | :func:`django.utils.timezone.now` useful. This function returns the current |
| 571 | date and time as a naive datetime when ``USE_TZ = False`` and as an aware |
| 572 | datetime when ``USE_TZ = True``. You can add or substract |
| 573 | :class:`datetime.timedelta` as needed. |
| 574 | |
| 575 | 2. **I see lots of** ``RuntimeWarning: DateTimeField received a naive |
| 576 | datetime`` ``(YYYY-MM-DD HH:MM:SS)`` ``while time zone support is active`` |
| 577 | **-- is it bad?** |
| 578 | |
| 579 | When time zone support is enabled, the database layer expects to receive |
| 580 | only aware datetimes from your code. This warning occurs when it receives a |
| 581 | naive datetime. This indicates that you haven't finished porting your code |
| 582 | for time zone support. Please refer to the :ref:`migration guide |
| 583 | <time-zones-migration-guide>` for tips on this process. |
| 584 | |
| 585 | In the meantime, for backwards compatibility, the datetime is considered to |
| 586 | be in the default time zone, which is generally what you expect. |
| 587 | |
| 588 | 3. ``now.date()`` **is yesterday! (or tomorrow)** |
| 589 | |
| 590 | If you've always used naive datetimes, you probably believe that you can |
| 591 | convert a datetime to a date by calling its :meth:`~datetime.datetime.date` |
| 592 | method. You also consider that a :class:`~datetime.date` is a lot like a |
| 593 | :class:`~datetime.datetime`, except that it's less accurate. |
| 594 | |
| 595 | None of this is true in a time zone aware environment:: |
| 596 | |
| 597 | >>> import datetime |
| 598 | >>> import pytz |
| 599 | >>> paris_tz = pytz.timezone("Europe/Paris") |
| 600 | >>> new_york_tz = pytz.timezone("America/New_York") |
| 601 | >>> paris = paris_tz.localize(datetime.datetime(2012, 3, 3, 1, 30)) |
| 602 | # This is the correct way to convert between time zones with pytz. |
| 603 | >>> new_york = new_york_tz.normalize(paris.astimezone(new_york_tz)) |
| 604 | >>> paris == new_york, paris.date() == new_york.date() |
| 605 | (True, False) |
| 606 | >>> paris - new_york, paris.date() - new_york.date() |
| 607 | (datetime.timedelta(0), datetime.timedelta(1)) |
| 608 | >>> paris |
| 609 | datetime.datetime(2012, 3, 3, 1, 30, tzinfo=<DstTzInfo 'Europe/Paris' CET+1:00:00 STD>) |
| 610 | >>> new_york |
| 611 | datetime.datetime(2012, 3, 2, 19, 30, tzinfo=<DstTzInfo 'America/New_York' EST-1 day, 19:00:00 STD>) |
| 612 | |
| 613 | As this example shows, the same datetime has a different date, depending on |
| 614 | the time zone in which it is representend. But the real problem is more fundamental. |
| 615 | |
| 616 | A datetime represents a **point in time**. It's absolute: it doesn't depend |
| 617 | on anything (barring relativistic effects). On the contrary, a date is a |
| 618 | **calendaring concept**. It's a period of time whose bounds depend on the |
| 619 | time zone in which the date is considered. As you can see, these two |
| 620 | concepts are fundamentally different and converting a datetime to a date |
| 621 | isn't a deterministic operation. |
| 622 | |
| 623 | What does this mean in practice? |
| 624 | |
| 625 | Generally, you should avoid converting a :class:`~datetime.datetime` to |
| 626 | :class:`~datetime.date`. For instance, you can use the :tfilter:`date` |
| 627 | template filter to only show the date part of a datetime. This filter will |
| 628 | convert the datetime into the current time zone before formatting it, |
| 629 | ensuring the results appear correct for the user. |
| 630 | |
| 631 | If you really need to do the conversion yourself, you must ensure the |
| 632 | datetime is converted to the appropriate time zone first. Usually, this |
| 633 | will be the current timezone:: |
| 634 | |
| 635 | >>> from django.utils import timezone |
| 636 | >>> timezone.activate(pytz.timezone("Asia/Singapore")) |
| 637 | # For this example, we just set the time zone to Singapore, but here's how |
| 638 | # you would obtain the current time zone in the general case. |
| 639 | >>> current_tz = timezone.get_current_timezone() |
| 640 | # Again, this is the correct way to convert between time zones with pytz. |
| 641 | >>> local = current_tz.normalize(paris.astimezone(current_tz)) |
| 642 | >>> local |
| 643 | datetime.datetime(2012, 3, 3, 8, 30, tzinfo=<DstTzInfo 'Asia/Singapore' SGT+8:00:00 STD>) |
| 644 | >>> local.date() |
| 645 | datetime.date(2012, 3, 3) |
| 646 | |
| 647 | Usage |
| 648 | ----- |
| 649 | |
| 650 | 1. **I have this string** ``"2012-02-21 10:28:45"`` **and I know it's in the** |
| 651 | ``"Europe/Helsinki"`` **time zone. How do I turn that into an aware |
| 652 | datetime?** |
| 653 | |
| 654 | This is exactly what pytz_ is for. |
| 655 | |
| 656 | >>> from django.utils.dateparse import parse_datetime |
| 657 | >>> naive = parse_datetime("2012-02-21 10:28:45") |
| 658 | >>> import pytz |
| 659 | >>> pytz.timezone("Europe/Helsinki").localize(naive) |
| 660 | datetime.datetime(2012, 2, 21, 10, 28, 45, tzinfo=<DstTzInfo 'Europe/Helsinki' EET+2:00:00 STD>) |
| 661 | |
| 662 | Note that ``localize`` is a pytz extension to the :class:`~datetime.tzinfo` |
| 663 | API. Also, you may want to catch :exc:`~pytz.InvalidTimeError`. The |
| 664 | documentation of pytz contains `more examples`_; you should review it |
| 665 | before attempting to manipulate aware datetimes. |
| 666 | |
| 667 | 2. **How can I obtain the current time in the local time zone?** |
| 668 | |
| 669 | Well, the first question is, do you really need to? |
| 670 | |
| 671 | You should only use local time when you're interacting with humans, and the |
| 672 | template layer provides :ref:`filters and tags <time-zones-in-templates>` |
| 673 | to convert datetimes to the time zone of your choice. |
| 674 | |
| 675 | Furthermore, Python knows how to compare aware datetimes, taking into |
| 676 | account UTC offsets when necessary. It's much easier (and possibly faster) |
| 677 | to write all your model and view code in UTC. So, in most circumstances, |
| 678 | the datetime in UTC returned by :func:`django.utils.timezone.now` will be |
| 679 | sufficient. |
| 680 | |
| 681 | Now, if you really want the current time in the local time zone, here's |
| 682 | the code:: |
| 683 | |
| 684 | >>> import datetime |
| 685 | >>> from django.utils import timezone |
| 686 | >>> datetime.datetime.now(tz=timezone.get_default_timezone()) |
| 687 | datetime.datetime(2012, 3, 3, 20, 10, 53, 873365, tzinfo=<DstTzInfo 'Europe/Paris' CET+1:00:00 STD>) |
| 688 | |
| 689 | In this example, pytz_ is installed and :setting:`TIME_ZONE` is |
| 690 | ``"Europe/Paris"``. |
| 691 | |
| 692 | 3. **How can I see all available time zones?** |
| 693 | |
| 694 | pytz_ provides helpers_, including a list of current time zones and a list |
| 695 | of all available time zones -- some of which are only of historical |
| 696 | interest. |
| 697 | |