123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710 |
- ==========
- Time zones
- ==========
- .. _time-zones-overview:
- Overview
- ========
- When support for time zones is enabled, Django stores datetime information in
- UTC in the database, uses time-zone-aware datetime objects internally, and
- translates them to the end user's time zone in templates and forms.
- This is handy if your users live in more than one time zone and you want to
- display datetime information according to each user's wall clock.
- Even if your website is available in only one time zone, it's still good
- practice to store data in UTC in your database. The main reason is daylight
- saving time (DST). Many countries have a system of DST, where clocks are moved
- forward in spring and backward in autumn. If you're working in local time,
- you're likely to encounter errors twice a year, when the transitions happen.
- This probably doesn't matter for your blog, but it's a problem if you over bill
- or under bill your customers by one hour, twice a year, every year. The
- solution to this problem is to use UTC in the code and use local time only when
- interacting with end users.
- Time zone support is enabled by default. To disable it, set :setting:`USE_TZ =
- False <USE_TZ>` in your settings file.
- .. versionchanged:: 5.0
- In older version, time zone support was disabled by default.
- Time zone support uses :mod:`zoneinfo`, which is part of the Python standard
- library from Python 3.9.
- If you're wrestling with a particular problem, start with the :ref:`time zone
- FAQ <time-zones-faq>`.
- Concepts
- ========
- .. _naive_vs_aware_datetimes:
- Naive and aware datetime objects
- --------------------------------
- Python's :class:`datetime.datetime` objects have a ``tzinfo`` attribute that
- can be used to store time zone information, represented as an instance of a
- subclass of :class:`datetime.tzinfo`. When this attribute is set and describes
- an offset, a datetime object is **aware**. Otherwise, it's **naive**.
- You can use :func:`~django.utils.timezone.is_aware` and
- :func:`~django.utils.timezone.is_naive` to determine whether datetimes are
- aware or naive.
- When time zone support is disabled, Django uses naive datetime objects in local
- time. This is sufficient for many use cases. In this mode, to obtain the
- current time, you would write::
- import datetime
- now = datetime.datetime.now()
- When time zone support is enabled (:setting:`USE_TZ=True <USE_TZ>`), Django uses
- time-zone-aware datetime objects. If your code creates datetime objects, they
- should be aware too. In this mode, the example above becomes::
- from django.utils import timezone
- now = timezone.now()
- .. warning::
- Dealing with aware datetime objects isn't always intuitive. For instance,
- the ``tzinfo`` argument of the standard datetime constructor doesn't work
- reliably for time zones with DST. Using UTC is generally safe; if you're
- using other time zones, you should review the :mod:`zoneinfo`
- documentation carefully.
- .. note::
- Python's :class:`datetime.time` objects also feature a ``tzinfo``
- attribute, and PostgreSQL has a matching ``time with time zone`` type.
- However, as PostgreSQL's docs put it, this type "exhibits properties which
- lead to questionable usefulness".
- Django only supports naive time objects and will raise an exception if you
- attempt to save an aware time object, as a timezone for a time with no
- associated date does not make sense.
- .. _naive-datetime-objects:
- Interpretation of naive datetime objects
- ----------------------------------------
- When :setting:`USE_TZ` is ``True``, Django still accepts naive datetime
- objects, in order to preserve backwards-compatibility. When the database layer
- receives one, it attempts to make it aware by interpreting it in the
- :ref:`default time zone <default-current-time-zone>` and raises a warning.
- Unfortunately, during DST transitions, some datetimes don't exist or are
- ambiguous. That's why you should always create aware datetime objects when time
- zone support is enabled. (See the :mod:`Using ZoneInfo section of the zoneinfo
- docs <zoneinfo>` for examples using the ``fold`` attribute to specify the
- offset that should apply to a datetime during a DST transition.)
- In practice, this is rarely an issue. Django gives you aware datetime objects
- in the models and forms, and most often, new datetime objects are created from
- existing ones through :class:`~datetime.timedelta` arithmetic. The only
- datetime that's often created in application code is the current time, and
- :func:`timezone.now() <django.utils.timezone.now>` automatically does the
- right thing.
- .. _default-current-time-zone:
- Default time zone and current time zone
- ---------------------------------------
- The **default time zone** is the time zone defined by the :setting:`TIME_ZONE`
- setting.
- The **current time zone** is the time zone that's used for rendering.
- You should set the current time zone to the end user's actual time zone with
- :func:`~django.utils.timezone.activate`. Otherwise, the default time zone is
- used.
- .. note::
- As explained in the documentation of :setting:`TIME_ZONE`, Django sets
- environment variables so that its process runs in the default time zone.
- This happens regardless of the value of :setting:`USE_TZ` and of the
- current time zone.
- When :setting:`USE_TZ` is ``True``, this is useful to preserve
- backwards-compatibility with applications that still rely on local time.
- However, :ref:`as explained above <naive-datetime-objects>`, this isn't
- entirely reliable, and you should always work with aware datetimes in UTC
- in your own code. For instance, use :meth:`~datetime.datetime.fromtimestamp`
- and set the ``tz`` parameter to :attr:`~datetime.timezone.utc`.
- Selecting the current time zone
- -------------------------------
- The current time zone is the equivalent of the current :term:`locale <locale
- name>` for translations. However, there's no equivalent of the
- ``Accept-Language`` HTTP header that Django could use to determine the user's
- time zone automatically. Instead, Django provides :ref:`time zone selection
- functions <time-zone-selection-functions>`. Use them to build the time zone
- selection logic that makes sense for you.
- Most websites that care about time zones ask users in which time zone they live
- and store this information in the user's profile. For anonymous users, they use
- the time zone of their primary audience or UTC.
- :func:`zoneinfo.available_timezones` provides a set of available timezones that
- you can use to build a map from likely locations to time zones.
- Here's an example that stores the current timezone in the session. (It skips
- error handling entirely for the sake of simplicity.)
- Add the following middleware to :setting:`MIDDLEWARE`::
- import zoneinfo
- from django.utils import timezone
- class TimezoneMiddleware:
- def __init__(self, get_response):
- self.get_response = get_response
- def __call__(self, request):
- tzname = request.session.get("django_timezone")
- if tzname:
- timezone.activate(zoneinfo.ZoneInfo(tzname))
- else:
- timezone.deactivate()
- return self.get_response(request)
- Create a view that can set the current timezone::
- from django.shortcuts import redirect, render
- # Prepare a map of common locations to timezone choices you wish to offer.
- common_timezones = {
- "London": "Europe/London",
- "Paris": "Europe/Paris",
- "New York": "America/New_York",
- }
- def set_timezone(request):
- if request.method == "POST":
- request.session["django_timezone"] = request.POST["timezone"]
- return redirect("/")
- else:
- return render(request, "template.html", {"timezones": common_timezones})
- Include a form in ``template.html`` that will ``POST`` to this view:
- .. code-block:: html+django
- {% load tz %}
- {% get_current_timezone as TIME_ZONE %}
- <form action="{% url 'set_timezone' %}" method="POST">
- {% csrf_token %}
- <label for="timezone">Time zone:</label>
- <select name="timezone">
- {% for city, tz in timezones %}
- <option value="{{ tz }}"{% if tz == TIME_ZONE %} selected{% endif %}>{{ city }}</option>
- {% endfor %}
- </select>
- <input type="submit" value="Set">
- </form>
- .. _time-zones-in-forms:
- Time zone aware input in forms
- ==============================
- When you enable time zone support, Django interprets datetimes entered in
- forms in the :ref:`current time zone <default-current-time-zone>` and returns
- aware datetime objects in ``cleaned_data``.
- Converted datetimes that don't exist or are ambiguous because they fall in a
- DST transition will be reported as invalid values.
- .. _time-zones-in-templates:
- Time zone aware output in templates
- ===================================
- When you enable time zone support, Django converts aware datetime objects to
- the :ref:`current time zone <default-current-time-zone>` when they're rendered
- in templates. This behaves very much like :doc:`format localization
- </topics/i18n/formatting>`.
- .. warning::
- Django doesn't convert naive datetime objects, because they could be
- ambiguous, and because your code should never produce naive datetimes when
- time zone support is enabled. However, you can force conversion with the
- template filters described below.
- Conversion to local time isn't always appropriate -- you may be generating
- output for computers rather than for humans. The following filters and tags,
- provided by the ``tz`` template tag library, allow you to control the time zone
- conversions.
- Template tags
- -------------
- .. templatetag:: localtime
- ``localtime``
- ~~~~~~~~~~~~~
- Enables or disables conversion of aware datetime objects to the current time
- zone in the contained block.
- This tag has exactly the same effects as the :setting:`USE_TZ` setting as far
- as the template engine is concerned. It allows a more fine grained control of
- conversion.
- To activate or deactivate conversion for a template block, use:
- .. code-block:: html+django
- {% load tz %}
- {% localtime on %}
- {{ value }}
- {% endlocaltime %}
- {% localtime off %}
- {{ value }}
- {% endlocaltime %}
- .. note::
- The value of :setting:`USE_TZ` isn't respected inside of a
- ``{% localtime %}`` block.
- .. templatetag:: timezone
- ``timezone``
- ~~~~~~~~~~~~
- Sets or unsets the current time zone in the contained block. When the current
- time zone is unset, the default time zone applies.
- .. code-block:: html+django
- {% load tz %}
- {% timezone "Europe/Paris" %}
- Paris time: {{ value }}
- {% endtimezone %}
- {% timezone None %}
- Server time: {{ value }}
- {% endtimezone %}
- .. templatetag:: get_current_timezone
- ``get_current_timezone``
- ~~~~~~~~~~~~~~~~~~~~~~~~
- You can get the name of the current time zone using the
- ``get_current_timezone`` tag:
- .. code-block:: html+django
- {% get_current_timezone as TIME_ZONE %}
- Alternatively, you can activate the
- :func:`~django.template.context_processors.tz` context processor and
- use the ``TIME_ZONE`` context variable.
- Template filters
- ----------------
- These filters accept both aware and naive datetimes. For conversion purposes,
- they assume that naive datetimes are in the default time zone. They always
- return aware datetimes.
- .. templatefilter:: localtime
- ``localtime``
- ~~~~~~~~~~~~~
- Forces conversion of a single value to the current time zone.
- For example:
- .. code-block:: html+django
- {% load tz %}
- {{ value|localtime }}
- .. templatefilter:: utc
- ``utc``
- ~~~~~~~
- Forces conversion of a single value to UTC.
- For example:
- .. code-block:: html+django
- {% load tz %}
- {{ value|utc }}
- .. templatefilter:: timezone
- ``timezone``
- ~~~~~~~~~~~~
- Forces conversion of a single value to an arbitrary timezone.
- The argument must be an instance of a :class:`~datetime.tzinfo` subclass or a
- time zone name.
- For example:
- .. code-block:: html+django
- {% load tz %}
- {{ value|timezone:"Europe/Paris" }}
- .. _time-zones-migration-guide:
- Migration guide
- ===============
- Here's how to migrate a project that was started before Django supported time
- zones.
- Database
- --------
- PostgreSQL
- ~~~~~~~~~~
- The PostgreSQL backend stores datetimes as ``timestamp with time zone``. In
- practice, this means it converts datetimes from the connection's time zone to
- UTC on storage, and from UTC to the connection's time zone on retrieval.
- As a consequence, if you're using PostgreSQL, you can switch between ``USE_TZ
- = False`` and ``USE_TZ = True`` freely. The database connection's time zone
- will be set to :setting:`DATABASE-TIME_ZONE` or ``UTC`` respectively, so that
- Django obtains correct datetimes in all cases. You don't need to perform any
- data conversions.
- .. admonition:: Time zone settings
- The :setting:`time zone <DATABASE-TIME_ZONE>` configured for the connection
- in the :setting:`DATABASES` setting is distinct from the general
- :setting:`TIME_ZONE` setting.
- Other databases
- ~~~~~~~~~~~~~~~
- Other backends store datetimes without time zone information. If you switch
- from ``USE_TZ = False`` to ``USE_TZ = True``, you must convert your data from
- local time to UTC -- which isn't deterministic if your local time has DST.
- Code
- ----
- The first step is to add :setting:`USE_TZ = True <USE_TZ>` to your settings
- file. At this point, things should mostly work. If you create naive datetime
- objects in your code, Django makes them aware when necessary.
- However, these conversions may fail around DST transitions, which means you
- aren't getting the full benefits of time zone support yet. Also, you're likely
- to run into a few problems because it's impossible to compare a naive datetime
- with an aware datetime. Since Django now gives you aware datetimes, you'll get
- exceptions wherever you compare a datetime that comes from a model or a form
- with a naive datetime that you've created in your code.
- So the second step is to refactor your code wherever you instantiate datetime
- objects to make them aware. This can be done incrementally.
- :mod:`django.utils.timezone` defines some handy helpers for compatibility
- code: :func:`~django.utils.timezone.now`,
- :func:`~django.utils.timezone.is_aware`,
- :func:`~django.utils.timezone.is_naive`,
- :func:`~django.utils.timezone.make_aware`, and
- :func:`~django.utils.timezone.make_naive`.
- Finally, in order to help you locate code that needs upgrading, Django raises
- a warning when you attempt to save a naive datetime to the database:
- .. code-block:: pytb
- RuntimeWarning: DateTimeField ModelName.field_name received a naive
- datetime (2012-01-01 00:00:00) while time zone support is active.
- During development, you can turn such warnings into exceptions and get a
- traceback by adding the following to your settings file::
- import warnings
- warnings.filterwarnings(
- "error",
- r"DateTimeField .* received a naive datetime",
- RuntimeWarning,
- r"django\.db\.models\.fields",
- )
- Fixtures
- --------
- When serializing an aware datetime, the UTC offset is included, like this::
- "2011-09-01T13:20:30+03:00"
- While for a naive datetime, it isn't::
- "2011-09-01T13:20:30"
- For models with :class:`~django.db.models.DateTimeField`\ s, this difference
- makes it impossible to write a fixture that works both with and without time
- zone support.
- Fixtures generated with ``USE_TZ = False``, or before Django 1.4, use the
- "naive" format. If your project contains such fixtures, after you enable time
- zone support, you'll see :exc:`RuntimeWarning`\ s when you load them. To get
- rid of the warnings, you must convert your fixtures to the "aware" format.
- You can regenerate fixtures with :djadmin:`loaddata` then :djadmin:`dumpdata`.
- Or, if they're small enough, you can edit them to add the UTC offset that
- matches your :setting:`TIME_ZONE` to each serialized datetime.
- .. _time-zones-faq:
- FAQ
- ===
- Setup
- -----
- #. **I don't need multiple time zones. Should I enable time zone support?**
- Yes. When time zone support is enabled, Django uses a more accurate model
- of local time. This shields you from subtle and unreproducible bugs around
- daylight saving time (DST) transitions.
- When you enable time zone support, you'll encounter some errors because
- you're using naive datetimes where Django expects aware datetimes. Such
- errors show up when running tests. You'll quickly learn how to avoid invalid
- operations.
- On the other hand, bugs caused by the lack of time zone support are much
- harder to prevent, diagnose and fix. Anything that involves scheduled tasks
- or datetime arithmetic is a candidate for subtle bugs that will bite you
- only once or twice a year.
- For these reasons, time zone support is enabled by default in new projects,
- and you should keep it unless you have a very good reason not to.
- #. **I've enabled time zone support. Am I safe?**
- Maybe. You're better protected from DST-related bugs, but you can still
- shoot yourself in the foot by carelessly turning naive datetimes into aware
- datetimes, and vice-versa.
- If your application connects to other systems -- for instance, if it queries
- a web service -- make sure datetimes are properly specified. To transmit
- datetimes safely, their representation should include the UTC offset, or
- their values should be in UTC (or both!).
- Finally, our calendar system contains interesting edge cases. For example,
- you can't always subtract one year directly from a given date:
- .. code-block:: pycon
- >>> import datetime
- >>> def one_year_before(value): # Wrong example.
- ... return value.replace(year=value.year - 1)
- ...
- >>> one_year_before(datetime.datetime(2012, 3, 1, 10, 0))
- datetime.datetime(2011, 3, 1, 10, 0)
- >>> one_year_before(datetime.datetime(2012, 2, 29, 10, 0))
- Traceback (most recent call last):
- ...
- ValueError: day is out of range for month
- To implement such a function correctly, you must decide whether 2012-02-29
- minus one year is 2011-02-28 or 2011-03-01, which depends on your business
- requirements.
- #. **How do I interact with a database that stores datetimes in local time?**
- Set the :setting:`TIME_ZONE <DATABASE-TIME_ZONE>` option to the appropriate
- time zone for this database in the :setting:`DATABASES` setting.
- This is useful for connecting to a database that doesn't support time zones
- and that isn't managed by Django when :setting:`USE_TZ` is ``True``.
- Troubleshooting
- ---------------
- #. **My application crashes with** ``TypeError: can't compare offset-naive``
- ``and offset-aware datetimes`` **-- what's wrong?**
- Let's reproduce this error by comparing a naive and an aware datetime:
- .. code-block:: pycon
- >>> from django.utils import timezone
- >>> aware = timezone.now()
- >>> naive = timezone.make_naive(aware)
- >>> naive == aware
- Traceback (most recent call last):
- ...
- TypeError: can't compare offset-naive and offset-aware datetimes
- If you encounter this error, most likely your code is comparing these two
- things:
- - a datetime provided by Django -- for instance, a value read from a form or
- a model field. Since you enabled time zone support, it's aware.
- - a datetime generated by your code, which is naive (or you wouldn't be
- reading this).
- Generally, the correct solution is to change your code to use an aware
- datetime instead.
- If you're writing a pluggable application that's expected to work
- independently of the value of :setting:`USE_TZ`, you may find
- :func:`django.utils.timezone.now` useful. This function returns the current
- date and time as a naive datetime when ``USE_TZ = False`` and as an aware
- datetime when ``USE_TZ = True``. You can add or subtract
- :class:`datetime.timedelta` as needed.
- #. **I see lots of** ``RuntimeWarning: DateTimeField received a naive
- datetime`` ``(YYYY-MM-DD HH:MM:SS)`` ``while time zone support is active``
- **-- is that bad?**
- When time zone support is enabled, the database layer expects to receive
- only aware datetimes from your code. This warning occurs when it receives a
- naive datetime. This indicates that you haven't finished porting your code
- for time zone support. Please refer to the :ref:`migration guide
- <time-zones-migration-guide>` for tips on this process.
- In the meantime, for backwards compatibility, the datetime is considered to
- be in the default time zone, which is generally what you expect.
- #. ``now.date()`` **is yesterday! (or tomorrow)**
- If you've always used naive datetimes, you probably believe that you can
- convert a datetime to a date by calling its :meth:`~datetime.datetime.date`
- method. You also consider that a :class:`~datetime.date` is a lot like a
- :class:`~datetime.datetime`, except that it's less accurate.
- None of this is true in a time zone aware environment:
- .. code-block:: pycon
- >>> import datetime
- >>> import zoneinfo
- >>> paris_tz = zoneinfo.ZoneInfo("Europe/Paris")
- >>> new_york_tz = zoneinfo.ZoneInfo("America/New_York")
- >>> paris = datetime.datetime(2012, 3, 3, 1, 30, tzinfo=paris_tz)
- # This is the correct way to convert between time zones.
- >>> new_york = paris.astimezone(new_york_tz)
- >>> paris == new_york, paris.date() == new_york.date()
- (True, False)
- >>> paris - new_york, paris.date() - new_york.date()
- (datetime.timedelta(0), datetime.timedelta(1))
- >>> paris
- datetime.datetime(2012, 3, 3, 1, 30, tzinfo=zoneinfo.ZoneInfo(key='Europe/Paris'))
- >>> new_york
- datetime.datetime(2012, 3, 2, 19, 30, tzinfo=zoneinfo.ZoneInfo(key='America/New_York'))
- As this example shows, the same datetime has a different date, depending on
- the time zone in which it is represented. But the real problem is more
- fundamental.
- A datetime represents a **point in time**. It's absolute: it doesn't depend
- on anything. On the contrary, a date is a **calendaring concept**. It's a
- period of time whose bounds depend on the time zone in which the date is
- considered. As you can see, these two concepts are fundamentally different,
- and converting a datetime to a date isn't a deterministic operation.
- What does this mean in practice?
- Generally, you should avoid converting a :class:`~datetime.datetime` to
- :class:`~datetime.date`. For instance, you can use the :tfilter:`date`
- template filter to only show the date part of a datetime. This filter will
- convert the datetime into the current time zone before formatting it,
- ensuring the results appear correctly.
- If you really need to do the conversion yourself, you must ensure the
- datetime is converted to the appropriate time zone first. Usually, this
- will be the current timezone:
- .. code-block:: pycon
- >>> from django.utils import timezone
- >>> timezone.activate(zoneinfo.ZoneInfo("Asia/Singapore"))
- # For this example, we set the time zone to Singapore, but here's how
- # you would obtain the current time zone in the general case.
- >>> current_tz = timezone.get_current_timezone()
- >>> local = paris.astimezone(current_tz)
- >>> local
- datetime.datetime(2012, 3, 3, 8, 30, tzinfo=zoneinfo.ZoneInfo(key='Asia/Singapore'))
- >>> local.date()
- datetime.date(2012, 3, 3)
- #. **I get an error** "``Are time zone definitions for your database
- installed?``"
- If you are using MySQL, see the :ref:`mysql-time-zone-definitions` section
- of the MySQL notes for instructions on loading time zone definitions.
- Usage
- -----
- #. **I have a string** ``"2012-02-21 10:28:45"`` **and I know it's in the**
- ``"Europe/Helsinki"`` **time zone. How do I turn that into an aware
- datetime?**
- Here you need to create the required ``ZoneInfo`` instance and attach it to
- the naïve datetime:
- .. code-block:: pycon
- >>> import zoneinfo
- >>> from django.utils.dateparse import parse_datetime
- >>> naive = parse_datetime("2012-02-21 10:28:45")
- >>> naive.replace(tzinfo=zoneinfo.ZoneInfo("Europe/Helsinki"))
- datetime.datetime(2012, 2, 21, 10, 28, 45, tzinfo=zoneinfo.ZoneInfo(key='Europe/Helsinki'))
- #. **How can I obtain the local time in the current time zone?**
- Well, the first question is, do you really need to?
- You should only use local time when you're interacting with humans, and the
- template layer provides :ref:`filters and tags <time-zones-in-templates>`
- to convert datetimes to the time zone of your choice.
- Furthermore, Python knows how to compare aware datetimes, taking into
- account UTC offsets when necessary. It's much easier (and possibly faster)
- to write all your model and view code in UTC. So, in most circumstances,
- the datetime in UTC returned by :func:`django.utils.timezone.now` will be
- sufficient.
- For the sake of completeness, though, if you really want the local time
- in the current time zone, here's how you can obtain it:
- .. code-block:: pycon
- >>> from django.utils import timezone
- >>> timezone.localtime(timezone.now())
- datetime.datetime(2012, 3, 3, 20, 10, 53, 873365, tzinfo=zoneinfo.ZoneInfo(key='Europe/Paris'))
- In this example, the current time zone is ``"Europe/Paris"``.
- #. **How can I see all available time zones?**
- :func:`zoneinfo.available_timezones` provides the set of all valid keys for
- IANA time zones available to your system. See the docs for usage
- considerations.
|