├── docs
├── _static
│ └── css
│ │ └── custom.css
├── api.rst
├── index.rst
├── Makefile
├── make.bat
├── quickstart.rst
├── conf.py
└── advanced.rst
├── MANIFEST.in
├── example
├── requirements.txt
├── app.py
└── templates
│ └── index.html
├── .readthedocs.yaml
├── .gitignore
├── README.md
├── tox.ini
├── LICENSE
├── pyproject.toml
├── .github
└── workflows
│ └── tests.yml
├── CHANGES.md
├── tests
└── test_flask_moment.py
└── src
└── flask_moment
└── __init__.py
/docs/_static/css/custom.css:
--------------------------------------------------------------------------------
1 | .py .class, .py .method, .py .property {
2 | margin-top: 20px;
3 | }
4 |
--------------------------------------------------------------------------------
/docs/api.rst:
--------------------------------------------------------------------------------
1 | API Reference
2 | -------------
3 |
4 | .. autoclass:: flask_moment.moment
5 | :members:
6 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include README.md LICENSE tox.ini
2 | recursive-include docs *
3 | recursive-exclude docs/_build *
4 | recursive-include tests *
5 | exclude **/*.pyc
6 |
--------------------------------------------------------------------------------
/example/requirements.txt:
--------------------------------------------------------------------------------
1 | click==8.0.1
2 | Flask==2.0.1
3 | Flask-Moment
4 | importlib-metadata==4.5.0
5 | itsdangerous==2.0.1
6 | Jinja2==3.0.1
7 | MarkupSafe==2.0.1
8 | pkg-resources==0.0.0
9 | typing-extensions==3.10.0.0
10 | Werkzeug==2.2.3
11 | zipp==3.4.1
12 |
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | build:
4 | os: ubuntu-22.04
5 | tools:
6 | python: "3.11"
7 |
8 | sphinx:
9 | configuration: docs/conf.py
10 |
11 | python:
12 | install:
13 | - method: pip
14 | path: .
15 | extra_requirements:
16 | - docs
17 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. Flask-Moment documentation master file, created by
2 | sphinx-quickstart on Wed Jun 9 23:10:03 2021.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Flask-Moment
7 | ============
8 |
9 | .. toctree::
10 | :maxdepth: 2
11 |
12 | quickstart
13 | advanced
14 | api
15 |
16 | * :ref:`genindex`
17 | * :ref:`search`
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.py[cod]
2 |
3 | # C extensions
4 | *.so
5 |
6 | # Packages
7 | *.egg
8 | *.egg-info
9 | dist
10 | build
11 | eggs
12 | parts
13 | bin
14 | var
15 | sdist
16 | develop-eggs
17 | .installed.cfg
18 | lib
19 | lib64
20 | __pycache__
21 | .cache
22 |
23 | # Installer logs
24 | pip-log.txt
25 |
26 | # Unit test / coverage reports
27 | .coverage
28 | .tox
29 | nosetests.xml
30 |
31 | # Translations
32 | *.mo
33 |
34 | # Mr Developer
35 | .mr.developer.cfg
36 | .project
37 | .pydevproject
38 |
39 | venv
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Flask-Moment
2 | ============
3 |
4 | [](https://github.com/miguelgrinberg/flask-moment/actions) [](https://codecov.io/gh/miguelgrinberg/flask-moment)
5 |
6 | This extension enhances Jinja2 templates with formatting of dates and times
7 | using [moment.js](https://momentjs.com/).
8 |
9 | Resources
10 | ---------
11 |
12 | - [Documentation](http://flask-moment.readthedocs.io/en/latest/)
13 | - [PyPI](https://pypi.python.org/pypi/flask-moment)
14 | - [Change Log](https://github.com/miguelgrinberg/flask-moment/blob/main/CHANGES.md)
15 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = .
9 | BUILDDIR = _build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist=flake8,py38,py39,py310,py311,py312,pypy3,docs
3 | skip_missing_interpreters=true
4 |
5 | [gh-actions]
6 | python =
7 | 3.7: py37
8 | 3.8: py38
9 | 3.9: py39
10 | 3.10: py310
11 | 3.11: py311
12 | pypy-3: pypy3
13 |
14 | [testenv]
15 | commands=
16 | pip install -e .
17 | pytest -p no:logging --cov=flask_moment --cov-branch --cov-report term-missing --cov-report=xml
18 | deps=
19 | pytest
20 | pytest-cov
21 |
22 | [testenv:flake8]
23 | commands=
24 | flake8 src/flask_moment tests example
25 | deps=
26 | flake8
27 |
28 | [testenv:docs]
29 | changedir=docs
30 | deps=
31 | sphinx
32 | allowlist_externals=
33 | make
34 | commands=
35 | make html
36 |
--------------------------------------------------------------------------------
/example/app.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime, timedelta
2 | from flask import Flask, render_template, jsonify
3 | from flask_moment import Moment
4 |
5 | app = Flask(__name__)
6 | moment = Moment(app)
7 |
8 |
9 | @app.route('/')
10 | def index():
11 | now = datetime.utcnow()
12 | midnight = datetime(now.year, now.month, now.day, 0, 0, 0)
13 | epoch = datetime(1970, 1, 1, 0, 0, 0)
14 | next_saturday = now + timedelta(5 - now.weekday())
15 | return render_template('index.html', now=now, midnight=midnight,
16 | epoch=epoch, next_saturday=next_saturday)
17 |
18 |
19 | @app.route('/ajax')
20 | def ajax():
21 | return jsonify({'timestamp': moment.create(datetime.utcnow()).format(
22 | 'LLLL')})
23 |
24 |
25 | if __name__ == '__main__':
26 | app.run(debug=True)
27 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=.
11 | set BUILDDIR=_build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Miguel Grinberg
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "Flask-Moment"
3 | version = "1.0.7.dev0"
4 | authors = [
5 | { name = "Miguel Grinberg", email = "miguel.grinberg@gmail.com" },
6 | ]
7 | description = "Formatting of dates and times in Flask templates using moment.js."
8 | classifiers = [
9 | "Intended Audience :: Developers",
10 | "Programming Language :: Python :: 3",
11 | "License :: OSI Approved :: MIT License",
12 | "Operating System :: OS Independent",
13 | ]
14 | requires-python = ">=3.6"
15 | dependencies = [
16 | "Flask",
17 | "packaging >=14.1",
18 | ]
19 |
20 | [project.readme]
21 | file = "README.md"
22 | content-type = "text/markdown"
23 |
24 | [project.urls]
25 | Homepage = "https://github.com/miguelgrinberg/flask-moment"
26 | "Bug Tracker" = "https://github.com/miguelgrinberg/flask-moment/issues"
27 |
28 | [project.optional-dependencies]
29 | docs = [
30 | "sphinx",
31 | ]
32 |
33 | [tool.setuptools]
34 | zip-safe = false
35 | include-package-data = true
36 |
37 | [tool.setuptools.package-dir]
38 | "" = "src"
39 |
40 | [tool.setuptools.packages.find]
41 | where = [
42 | "src",
43 | ]
44 | namespaces = false
45 |
46 | [build-system]
47 | requires = [
48 | "setuptools>=61.2",
49 | ]
50 | build-backend = "setuptools.build_meta"
51 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: build
2 | on:
3 | push:
4 | branches:
5 | - main
6 | pull_request:
7 | branches:
8 | - main
9 | jobs:
10 | lint:
11 | name: lint
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v3
15 | - uses: actions/setup-python@v3
16 | - run: python -m pip install --upgrade pip wheel
17 | - run: pip install tox tox-gh-actions
18 | - run: tox -eflake8
19 | tests:
20 | name: tests
21 | strategy:
22 | matrix:
23 | os: [ubuntu-latest, macos-latest, windows-latest]
24 | python: ['3.8', '3.9', '3.10', '3.11', '3.12', 'pypy-3.10']
25 | fail-fast: false
26 | runs-on: ${{ matrix.os }}
27 | steps:
28 | - uses: actions/checkout@v3
29 | - uses: actions/setup-python@v3
30 | with:
31 | python-version: ${{ matrix.python }}
32 | - run: python -m pip install --upgrade pip wheel
33 | - run: pip install tox tox-gh-actions
34 | - run: tox
35 | coverage:
36 | name: coverage
37 | runs-on: ubuntu-latest
38 | steps:
39 | - uses: actions/checkout@v3
40 | - uses: actions/setup-python@v3
41 | - run: python -m pip install --upgrade pip wheel
42 | - run: pip install tox tox-gh-actions
43 | - run: tox
44 | - uses: codecov/codecov-action@v3
45 | with:
46 | files: ./coverage.xml
47 | fail_ci_if_error: true
48 | token: ${{ secrets.CODECOV_TOKEN }}
49 |
--------------------------------------------------------------------------------
/example/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Flask-Moment example app
4 | {{ moment.include_moment() }}
5 |
20 |
21 |
22 | The current date and time is: {{ moment().format('MMMM Do YYYY, h:mm:ss a') }}.
23 |
24 | {{ moment(midnight).fromTime(now) }} it was midnight in the UTC timezone.
25 | That was {{ moment(midnight).calendar() }} in your local time.
26 | It will be midnight again {{ moment(midnight).toNow() }}.
27 |
28 |
29 | Unix epoch is {{ moment(epoch, local=True).format('LLLL') }} in the UTC timezone.
30 | That was {{ moment(epoch).format('LLLL') }} in your local time.
31 |
32 |
33 | This page was rendered on {{ moment(now).format('LLL') }},
34 | which was {{ moment(now).fromNow(refresh = True) }}.
35 |
36 |
37 | The next Saturday will be in {{ moment(now).toTime(next_saturday) }}, from the time this page was rendered.
38 |
39 |
40 | Time difference between now and next Saturday: {{ moment(next_saturday).diff(now, 'days') }} days
41 | or {{ moment(next_saturday).diff(now, 'hours') }} hours.
42 |
43 |
44 | Load Ajax timestamp
45 |
46 |
47 |
--------------------------------------------------------------------------------
/docs/quickstart.rst:
--------------------------------------------------------------------------------
1 | Quick Start
2 | -----------
3 |
4 | Installation and configuration
5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6 |
7 | Install the extension with ``pip``::
8 |
9 |
10 | pip install flask-moment
11 |
12 | Once the extension is installed, create an instance and initialize it with the
13 | Flask application::
14 |
15 | from flask_moment import Moment
16 | moment = Moment(app)
17 |
18 | If the application uses the `application factory pattern `_,
19 | the two-step initialization method can be used instead::
20 |
21 | moment = Moment()
22 |
23 | def create_app():
24 | app = Flask(__name__)
25 | moment.init_app(app)
26 | return app
27 |
28 | app = create_app(prod_config)
29 |
30 | To complete the initialization, add the following line inside the ````
31 | section of the template(s) that will use the extension::
32 |
33 | {{ moment.include_moment() }}
34 |
35 | If you use template inheritance, the best place to add this line is in your
36 | base template.
37 |
38 | Note that older versions of this extension required jQuery, but this isn't the
39 | case anymore. The ``include_jquery()`` function that existed in older releases
40 | has now been removed.
41 |
42 | The ``include_moment()`` function accepts a few arguments that control settings
43 | such as the version of ``moment.js`` to use, or whether the library should be
44 | imported from a CDN.
45 |
46 | Rendering timestamps with Flask-Moment
47 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
48 |
49 | To render a date with Flask-Moment, use the ``moment()`` function with one of
50 | the available formatting options. The following example uses a custom format
51 | string to render the current time::
52 |
53 | Current date and time: {{ moment().format('MMMM Do YYYY, h:mm:ss a') }}.
54 |
55 | To render a timestamp other than the current time, pass a ``datetime`` object
56 | as an argument to the ``moment()`` function::
57 |
58 | Date: {{ moment(date).format('LL') }}
59 |
60 | Some of the rendering functions render the elapsed time between a datetime
61 | object and current time. The following example renders a timestamp in a
62 | "time ago" style::
63 |
64 | {{ author }} said {{ moment(comment_datetime).fromNow() }}
65 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # This file only contains a selection of the most common options. For a full
4 | # list see the documentation:
5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
6 |
7 | # -- Path setup --------------------------------------------------------------
8 |
9 | # If extensions (or modules to document with autodoc) are in another directory,
10 | # add these directories to sys.path here. If the directory is relative to the
11 | # documentation root, use os.path.abspath to make it absolute, like shown here.
12 | #
13 | import os
14 | import sys
15 | sys.path.insert(0, os.path.abspath('../src'))
16 |
17 |
18 | # -- Project information -----------------------------------------------------
19 |
20 | project = 'Flask-Moment'
21 | copyright = '2021, Miguel Grinberg'
22 | author = 'Miguel Grinberg'
23 |
24 |
25 | # -- General configuration ---------------------------------------------------
26 |
27 | # Add any Sphinx extension module names here, as strings. They can be
28 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
29 | # ones.
30 | extensions = [
31 | 'sphinx.ext.autodoc',
32 | ]
33 |
34 | # Add any paths that contain templates here, relative to this directory.
35 | templates_path = ['_templates']
36 |
37 | # List of patterns, relative to source directory, that match files and
38 | # directories to ignore when looking for source files.
39 | # This pattern also affects html_static_path and html_extra_path.
40 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
41 |
42 |
43 | # -- Options for HTML output -------------------------------------------------
44 |
45 | # The theme to use for HTML and HTML Help pages. See the documentation for
46 | # a list of builtin themes.
47 | #
48 | html_theme = 'alabaster'
49 |
50 | # Add any paths that contain custom static files (such as style sheets) here,
51 | # relative to this directory. They are copied after the builtin static files,
52 | # so a file named "default.css" will overwrite the builtin "default.css".
53 | html_static_path = ['_static']
54 |
55 | html_css_files = [
56 | 'css/custom.css',
57 | ]
58 |
59 | html_theme_options = {
60 | 'github_user': 'miguelgrinberg',
61 | 'github_repo': 'flask-moment',
62 | 'github_banner': True,
63 | 'github_button': True,
64 | 'github_type': 'star',
65 | 'fixed_sidebar': True,
66 | }
67 |
68 | autodoc_default_options = {
69 | 'member-order': 'bysource',
70 | }
71 |
72 | add_module_names = False
73 |
--------------------------------------------------------------------------------
/docs/advanced.rst:
--------------------------------------------------------------------------------
1 | Advanced options
2 | ----------------
3 |
4 | Auto-Refresh
5 | ~~~~~~~~~~~~
6 |
7 | All the display functions take an optional ``refresh`` argument that when set
8 | to ``True`` will re-render timestamps every minute. If a different time
9 | interval is desired, pass the interval in minutes as the value of the
10 | ``refresh`` argument.
11 |
12 | This option is useful for relative time formats such as the one returned by
13 | the ``fromNow()`` or ``fromTime()`` functions. For example, the date of a
14 | comment would appear as "a few seconds ago" when the comment was just entered,
15 | but a minute later it would automatically refresh to "a minute ago" without
16 | the need to refresh the page.
17 |
18 | By default automatic refreshing is disabled.
19 |
20 | Default Format
21 | ~~~~~~~~~~~~~~
22 |
23 | The ``format()`` function can be invoked without arguments, in which case a
24 | default format of ISO8601 defined by the moment.js library is used. If you
25 | want to set a different default, you can set the ``MOMENT_DEFAULT_FORMAT``
26 | variable in the Flask configuration. Consult the
27 | `moment.js format documentation `_
28 | for a list of accepted tokens.
29 |
30 | Internationalization
31 | ~~~~~~~~~~~~~~~~~~~~
32 |
33 | By default dates and times are rendered in English. To change to a different
34 | language add the following line in the ```` section of your template(s),
35 | after the ``include_moment()`` line::
36 |
37 | {{ moment.locale("es") }}
38 |
39 | The above example sets the language to Spanish. Moment.js supports a large
40 | number of languages. Consult the documentation for the list of languages and
41 | their corresponding two letter codes.
42 |
43 | This extension also supports auto-detection of the client's browser language::
44 |
45 | {{ moment.locale(auto_detect=True) }}
46 |
47 | Custom locales can also be included as a dictionary::
48 |
49 | {{ moment.locale(customizations={ ... }) }}
50 |
51 | See the `moment.js locale customizations `_
52 | documentation for details on how to define a custom locale.
53 |
54 | Ajax Support
55 | ~~~~~~~~~~~~
56 |
57 | It is also possible to create Flask-Moment timestamps in Python code that is
58 | invoked from the client page through Ajax, without the use of Jinja templates::
59 |
60 |
61 | timestamp = moment.create(datetime.utcnow()).calendar()
62 |
63 | The ``moment`` variable is the ``Moment`` instance that was created at
64 | initialization time.
65 |
66 | A timestamp created in this way is an HTML string that can be returned as part
67 | of a response. For example, here is how a timestamp can be returned in a JSON
68 | object::
69 |
70 | return { 'timestamp': moment.create(datetime.utcnow()).format('L') }
71 |
72 | The Ajax callback in the browser needs to call ``flask_moment_render_all()``
73 | each time an element containing a timestamp is added to the DOM. The example
74 | application in the Flask-Moment GitHub repository demonstrates how this is
75 | done.
76 |
77 | Subresource Integrity (SRI)
78 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
79 |
80 | `SRI `_
81 | is a security feature that enables browsers to verify that resources they
82 | fetch are not maliciously manipulated. To do so a cryptographic hash is
83 | provided that proves integrity.
84 |
85 | SRI for the moment.js library is enabled by default. If you wish to use
86 | a version different than the one bundled with this extension, or want to host
87 | your own javascript, a `separate hash `_
88 | can be provided.
89 |
90 | Just add ``sri=`` when calling ``moment.include_moment()``. If no
91 | SRI hash is provided when a custom moment.js version is used, then SRI
92 | verification is not used.
93 |
94 | To disable SRI, pass ``sri=False`` in the ``include_moment()`` call.
95 |
--------------------------------------------------------------------------------
/CHANGES.md:
--------------------------------------------------------------------------------
1 | # Flask-Moment change log
2 |
3 | **Release 1.0.6** - 2024-05-28
4 |
5 | - Remove use of deprecated `datetime.utcnow()` function ([commit](https://github.com/miguelgrinberg/flask-moment/commit/3216125c9bcf8efa4b6c33746af20a49a84ddcab))
6 | - Support timestamps passed as ISO 8601 strings [#94](https://github.com/miguelgrinberg/flask-moment/issues/94) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/e9663bbeb810f0b37da1d210fbfc91e7327a3f2e))
7 |
8 | **Release 1.0.5** - 2022-10-05
9 |
10 | - Upgrade moment.js to version 2.29.4 [#89](https://github.com/miguelgrinberg/flask-moment/issues/89) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/4483b94442ce165b7b83afbed9ed9a9a513658bc)) (thanks **Ezequiel Parziale**!)
11 | - Fix documentation typos [#88](https://github.com/miguelgrinberg/flask-moment/issues/88) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/3fda3bc3640d57fc105c69e2ad6ef8af7aa430c0)) (thanks **Tim Gates**!)
12 |
13 | **Release 1.0.4** - 2022-07-17
14 |
15 | - Add packaging as a dependency [#86](https://github.com/miguelgrinberg/flask-moment/issues/86) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/e7bc9332eff0e78c9312100ad8540e2cdda600a9)) (thanks **Chuxiao Feng**!)
16 |
17 | **Release 1.0.3** - 2022-07-16
18 |
19 | - Return the raw JavaScript code to enable unsupported use cases [#84](https://github.com/miguelgrinberg/flask-moment/issues/84) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/c79961d46f3b69c7ffc217c2bb35f8289e54b8c4))
20 | - Remove deprecated `StrictVersion` usage [#85](https://github.com/miguelgrinberg/flask-moment/issues/85) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/02a8392876f84c439f548fe639a8890aef0ede97))
21 |
22 | **Release 1.0.2** - 2021-07-16
23 |
24 | - High CPU usage when refresh is disabled [#81](https://github.com/miguelgrinberg/flask-moment/issues/81) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/7ffce2fea01c25d72b4417b408b6cca5119b8a3e))
25 |
26 | **Release 1.0.1** - 2021-06-11
27 |
28 | - Fix package metadata for release [#80](https://github.com/miguelgrinberg/flask-moment/issues/80) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/19d601e87eae4c0bd7d34f4734de46b931e8b1b2))
29 |
30 | **Release 1.0.0** - 2021-06-11
31 |
32 | - Remove dependency on jQuery ([commit](https://github.com/miguelgrinberg/flask-moment/commit/3ab78505303ca5ecf1de1324893918f3b541f2d6)) (thanks **yuxiaoy**!)
33 | - Add `diff()` function [#78](https://github.com/miguelgrinberg/flask-moment/issues/78) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/79d8f43a6f0d31ce9a81d39954bb3f2f08923028)) (thanks **valerii**!)
34 | - Import Markup from markupsafe [#71](https://github.com/miguelgrinberg/flask-moment/issues/71) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/b1c48c41d6783585096003dd22bfd0d121ea306e)) (thanks **jn**!)
35 | - Fix Markup in unit tests [#72](https://github.com/miguelgrinberg/flask-moment/issues/72) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/7566c6b6c2d09ca3b06e3116dd7b5f35ce37b83e)) (thanks **Valerii Tryhubov**!)
36 | - Sphinx documentation ([commit](https://github.com/miguelgrinberg/flask-moment/commit/e9afd81a602cbf1962af307a6d5b5556ee827381))
37 | - Update requirements for example application ([commit](https://github.com/miguelgrinberg/flask-moment/commit/1cdc1f16eed3ad945d2774b925a4f9e2f2a54644))
38 | - Improved project structure ([commit](https://github.com/miguelgrinberg/flask-moment/commit/f79481c84eae62d7d0c8606bfa235f5b223111cf))
39 | - Update links in documentation [#79](https://github.com/miguelgrinberg/flask-moment/issues/79) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/335d3b3cea648f0eb90137b1cdd3840b7cb737ee)) (thanks **Frank Yu**!)
40 | - Unit test reorganization ([commit](https://github.com/miguelgrinberg/flask-moment/commit/e29403f78b326b70445ef4659b42aea29de67604))
41 | - GitHub Actions builds ([commit](https://github.com/miguelgrinberg/flask-moment/commit/1886b76330fd9d1f07985f588ecc5ceaeec5c9fc))
42 | - Fix documentation typos [#74](https://github.com/miguelgrinberg/flask-moment/issues/74) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/13a35e259292666210445018ea72e6d1b0579486)) (thanks **yuxiaoy**!)
43 |
44 | **Release 0.11.0** - 2020-12-17
45 |
46 | - Update default moment version and hash [#67](https://github.com/miguelgrinberg/flask-moment/issues/67) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/f4ceefbdab6c9dcf1449127e7cdf7a3aa0049da3)) (thanks **Irakliy Krasnov**!)
47 | - Always use `https://` to request the JavaScript files from the CDN [#65](https://github.com/miguelgrinberg/flask-moment/issues/65) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/a8f42fce6b1f50b73694830975a29bb30d95efdd)) (thanks **Vicki Jackson**!)
48 | - Update moment.js and add option to use Moment.js without locales [#63](https://github.com/miguelgrinberg/flask-moment/issues/63) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/5472239042d674809fc62dd3112d52f8154d3ec9)) (thanks **Steven van de Graaf**!)
49 |
50 | **Release 0.10.0** - 2020-06-10
51 |
52 | - Various code and test improvements, also add python 3.7 and 3.8 to build ([commit](https://github.com/miguelgrinberg/flask-moment/commit/842185179d2b89f895281b0b4077a8eabeba6f83))
53 | - Remove use of JavaScript eval[#60](https://github.com/miguelgrinberg/flask-moment/issues/60) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/5e453fc0e9a0947662ceaa0fca50cab090ca3811)) (thanks **Emilien Klein**!)
54 | - Docs: Fix simple typo, acepted -> accepted [#58](https://github.com/miguelgrinberg/flask-moment/issues/58) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/e506753ffe9d0b5200173f714c0294754b685699)) (thanks **Tim Gates**!)
55 | - Add `toTime` and `toNow` to display functions [#57](https://github.com/miguelgrinberg/flask-moment/issues/57) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/13ba7ee3b0ab8d0e7aa774781ef3b3ec33cad9ce)) (thanks **Mohamed Feddad**!)
56 |
57 | **Release 0.9.0** - 2019-08-04
58 |
59 | - Updated requirements in example application ([commit](https://github.com/miguelgrinberg/flask-moment/commit/28a5fdbcffcd8b6ed03d9e67bdbf31328b57e2d6))
60 | - Add support of customization object for the method moment.locale [#50](https://github.com/miguelgrinberg/flask-moment/issues/50) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/5b274132604f6e2d8dd5b4c7adb221b17c42c817)) (thanks **Aleksandr**!)
61 |
62 | **Release 0.8.0** - 2019-06-16
63 |
64 | - Default format [#48](https://github.com/miguelgrinberg/flask-moment/issues/48) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/8786663286806668eab3683458aa3390d505484b)) (thanks **jacobthetechy**!)
65 | - Simplify tests and handling of js versions ([commit](https://github.com/miguelgrinberg/flask-moment/commit/1b273c445957ea507ee23926dfa17be111b74fd7))
66 | - Add SRI v2.0 [#42](https://github.com/miguelgrinberg/flask-moment/issues/42) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/d87eaf8e485757a871928e5248d07a586abfd5fc)) (thanks **M0r13n**!)
67 |
68 | **Release 0.7.0** - 2019-02-16
69 |
70 | - Added `no_js` argument to `include_moment` ([commit](https://github.com/miguelgrinberg/flask-moment/commit/d99f9ba7c393fa120afff10a9bded11d09a303d5))
71 | - Fix license declaration in setup.py ([commit](https://github.com/miguelgrinberg/flask-moment/commit/243d8e0d523e4f060a7c2cf1ce7b8135bdcefaf1))
72 |
73 | **Release 0.6.0** - 2017-12-28
74 |
75 | - Fix travis build ([commit](https://github.com/miguelgrinberg/flask-moment/commit/d0468b770d2cbdedf92e051053793c186eb9f6ba))
76 | - Add auto-detect locale support [#33](https://github.com/miguelgrinberg/flask-moment/issues/33) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/32e18b6fc6594ba34281cef04356a5d24bfdf5a2)) (thanks **Grey Li**!)
77 | - Fixed unit tests ([commit](https://github.com/miguelgrinberg/flask-moment/commit/6503a2f53cccba498df9675f3ebceaae01dc5c1c))
78 |
79 | **Release 0.5.2** - 2017-09-29
80 |
81 | - Bump moment.js version to 2.18.1 ([commit](https://github.com/miguelgrinberg/flask-moment/commit/c412f13e1235ab4ec5f07fd01a8dcf56b9a7ad51))
82 | - Travis build badge ([commit](https://github.com/miguelgrinberg/flask-moment/commit/bbd622383b63892702f337772e45f1443bf610c7))
83 | - A few unit test fixes, plus tox/travis builds ([commit](https://github.com/miguelgrinberg/flask-moment/commit/71f46e56ff60d2ae66f189909de3544d7aca35ec))
84 | - Add tests for the basic features [#28](https://github.com/miguelgrinberg/flask-moment/issues/28) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/e908e2befa456f4fd886845e52060b643b3ecf04)) (thanks **Kyle Lawlor**!)
85 | - Added example of how to setup Flask-Moment using a Flask app factory ([commit](https://github.com/miguelgrinberg/flask-moment/commit/5a27fff48864d7233b05936b54edbc3212f0938d)) (thanks **Jeff Widman**!)
86 | - Switched imports from deprecated `flask.ext.extensionname` format to `flask_extensionname` ([commit](https://github.com/miguelgrinberg/flask-moment/commit/32f8efc9079a5e7eedf05abba72d42ffc806d4c3)) (thanks **Jeff Widman**!)
87 |
88 | **Release 0.5.1** - 2015-08-06
89 |
90 | - Hide timestamps until rendering is complete [#16](https://github.com/miguelgrinberg/flask-moment/issues/16) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/a5d312f789949e352845dd1f767e98a8e428b3eb)) (thanks **Cole Kettler**!)
91 | - Upgrading the default version to the latest moment.js release(2.10.3) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/56102d5b53c8d2b95b3684661517cc0cb2a6c713)) (thanks **small-yellow-rice**!)
92 |
93 | **Release 0.4.0**
94 |
95 | - Added `local_js` option to `include_moment()` and `include_jquery()` ([commit](https://github.com/miguelgrinberg/flask-moment/commit/8bd3035ec4d234ac7617e22e46efc06936cd0db2))
96 | - pep8 fixes ([commit](https://github.com/miguelgrinberg/flask-moment/commit/ff63a40e97c91a1432101125c6b26cc40f2b62d2))
97 |
98 | **Release 0.3.3**
99 |
100 | - Correct fix for [#8](https://github.com/miguelgrinberg/flask-moment/issues/8) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/0d3dcd8a550bb7961d3a1469ed80b24bcf277466))
101 |
102 | **Release 0.3.2**
103 |
104 | - Correct `include_moment(version=None)` handling [#8](https://github.com/miguelgrinberg/flask-moment/issues/8) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/e59dae390e4fccb30ef761ff18e29803fe3d9e57))
105 |
106 | **Release 0.3.1**
107 |
108 | - Fixed https support ([commit](https://github.com/miguelgrinberg/flask-moment/commit/1ccf8aec18f3325eb9d74468f904cd10b9c31f27)) (thanks **Tal Shiri**!)
109 |
110 | **Release 0.3**
111 |
112 | - Added "local" argument to `moment()` constructor to disable conversion from UTC to local time ([commit](https://github.com/miguelgrinberg/flask-moment/commit/3afcbc6290494402b420a0a98bae2be94a7565f1))
113 | - Use secure URLs when request is secure [#2](https://github.com/miguelgrinberg/flask-moment/issues/2) ([commit](https://github.com/miguelgrinberg/flask-moment/commit/0ee69da4408171168c6c0fe84d6f437ab4e8031f))
114 |
115 | **Release 0.2**
116 |
117 | - First public version
118 |
--------------------------------------------------------------------------------
/tests/test_flask_moment.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | import unittest
3 | from unittest import mock
4 |
5 | from flask import Flask, render_template_string
6 | from markupsafe import Markup
7 |
8 | from flask_moment import Moment, default_moment_version, default_moment_sri
9 |
10 |
11 | class TestMoment(unittest.TestCase):
12 | def setUp(self):
13 | self.app = Flask(__name__)
14 | self.moment_app = Moment(self.app)
15 | self.appctx = self.app.app_context()
16 | self.moment = self.app.extensions['moment']
17 | self.appctx.push()
18 |
19 | def tearDown(self):
20 | self.appctx.pop()
21 |
22 | def test_init(self):
23 | assert self.app.extensions['moment'].__name__ == 'moment'
24 | assert self.app.template_context_processors[None][1].__globals__[
25 | '__name__'] == 'flask_moment'
26 |
27 | def test_init_app(self):
28 | app = Flask(__name__)
29 | moment = Moment()
30 | moment.init_app(app)
31 | assert app.extensions['moment'].__name__ == 'moment'
32 | assert app.template_context_processors[None][1].__globals__[
33 | '__name__'] == 'flask_moment'
34 |
35 | def test_include_moment_directly(self):
36 | include_moment = self.moment.include_moment()
37 | assert isinstance(include_moment, Markup)
38 | assert '' in str(
53 | include_moment)
54 |
55 | def test_include_moment_renders_properly(self):
56 | ts = str(render_template_string('{{ moment.include_moment() }}'))
57 | assert ''.format(
288 | default_moment_version, default_moment_sri))
289 |
290 | def test_moment_from_cdn_with_custom_sri_hash(self):
291 | include_moment = self.moment.include_moment(sri='sha384-12345678')
292 | assert include_moment.startswith(
293 | ''.format(default_moment_version))
296 |
297 | include_moment = self.moment.include_moment(version='2.0.0',
298 | sri='sha384-12345678')
299 | assert include_moment.startswith(
300 | '')
303 |
304 | def test_moment_local(self):
305 | include_moment = self.moment.include_moment(local_js=True)
306 | assert 'src=\"' in include_moment
307 | assert 'integrity=\"' not in include_moment
308 | assert 'crossorigin\"' not in include_moment
309 |
310 | def test_moment_local_with_sri(self):
311 | include_moment = self.moment.include_moment(local_js=True,
312 | sri='sha384-87654321')
313 | assert 'src=\"' in include_moment
314 | assert 'integrity=\"sha384-87654321\"' in include_moment
315 | assert 'crossorigin=\"anonymous\"' in include_moment
316 |
317 | def test_disabling_moment_default(self):
318 | include_moment = self.moment.include_moment(sri=False)
319 | assert 'src=\"' in include_moment
320 | assert 'integrity=\"' not in include_moment
321 | assert 'crossorigin' not in include_moment
322 |
323 | def test_disabling_moment_custom(self):
324 | include_moment = self.moment.include_moment(local_js=True, sri=False)
325 | assert 'src=\"' in include_moment
326 | assert 'integrity=\"' not in include_moment
327 | assert 'crossorigin' not in include_moment
328 |
329 | def test_disabling_moment_custom_version(self):
330 | include_moment = self.moment.include_moment(version='2.17.9',
331 | sri=False)
332 | assert 'src=\"' in include_moment
333 | assert 'integrity=\"' not in include_moment
334 | assert 'crossorigin' not in include_moment
335 |
--------------------------------------------------------------------------------
/src/flask_moment/__init__.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime, timezone
2 | from packaging.version import parse as version_parse
3 | from markupsafe import Markup
4 | from flask import current_app
5 |
6 | # //cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment-with-locales.min.js
7 | default_moment_version = '2.29.4'
8 | default_moment_sri = ('sha512-42PE0rd+wZ2hNXftlM78BSehIGzezNeQuzihiBCvUEB3CVx'
9 | 'HvsShF86wBWwQORNxNINlBPuq7rG4WWhNiTVHFg==')
10 |
11 | js_code = '''function flask_moment_render(elem) {{
12 | const timestamp = moment(elem.dataset.timestamp);
13 | const func = elem.dataset.function;
14 | const format = elem.dataset.format;
15 | const timestamp2 = elem.dataset.timestamp2;
16 | const no_suffix = elem.dataset.nosuffix;
17 | const units = elem.dataset.units;
18 | let args = [];
19 | if (format)
20 | args.push(format);
21 | if (timestamp2)
22 | args.push(moment(timestamp2));
23 | if (no_suffix)
24 | args.push(no_suffix);
25 | if (units)
26 | args.push(units);
27 | elem.textContent = timestamp[func].apply(timestamp, args);
28 | elem.classList.remove('flask-moment');
29 | elem.style.display = "";
30 | }}
31 | function flask_moment_render_all() {{
32 | const moments = document.querySelectorAll('.flask-moment');
33 | moments.forEach(function(moment) {{
34 | flask_moment_render(moment);
35 | const refresh = moment.dataset.refresh;
36 | if (refresh && refresh > 0) {{
37 | (function(elem, interval) {{
38 | setInterval(function() {{
39 | flask_moment_render(elem);
40 | }}, interval);
41 | }})(moment, refresh);
42 | }}
43 | }})
44 | }}
45 | document.addEventListener("DOMContentLoaded", flask_moment_render_all);'''
46 |
47 |
48 | def _naive_now():
49 | """Return a naive datetime object set to current UTC time."""
50 | return datetime.now(timezone.utc).replace(tzinfo=None)
51 |
52 |
53 | class moment(object):
54 | """Create a moment object.
55 |
56 | :param timestamp: The ``datetime`` object or ISO 8601 string representing
57 | the timestamp.
58 | :param local: If ``True``, the ``timestamp`` argument is given in the
59 | local client time. In most cases this argument will be set
60 | to ``False`` and all the timestamps managed by the server
61 | will be in the UTC timezone.
62 | """
63 | @classmethod
64 | def include_moment(cls, version=default_moment_version, local_js=None,
65 | no_js=None, sri=None, with_locales=True):
66 | """Include the moment.js library and the supporting JavaScript code
67 | used by this extension.
68 |
69 | This function must be called in the ```` section of the Jinja
70 | template(s) that use this extension.
71 |
72 | :param version: The version of moment.js to include.
73 | :param local_js: The URL to import the moment.js library from. Use this
74 | option to import the library from a locally hosted
75 | file.
76 | :param no_js: Just add the supporting code for this extension, without
77 | importing the moment.js library. . Use this option if
78 | the library is imported elsewhere in the template. The
79 | supporting JavaScript code for this extension is still
80 | included.
81 | :param sri: The SRI hash to use when importing the moment.js library,
82 | or ``None`` if the SRI hash is unknown or disabled.
83 | :param with_locales: If ``True``, include the version of moment.js that
84 | has all the locales.
85 | """
86 | mjs = ''
87 | if version == default_moment_version and local_js is None and \
88 | with_locales is True and sri is None:
89 | sri = default_moment_sri
90 | if not no_js:
91 | if local_js is not None:
92 | if not sri:
93 | mjs = '\n'.format(local_js)
94 | else:
95 | mjs = ('\n').format(
97 | local_js, sri)
98 | elif version is not None:
99 | if with_locales:
100 | js_filename = 'moment-with-locales.min.js' \
101 | if version_parse(version) >= version_parse('2.8.0') \
102 | else 'moment-with-langs.min.js'
103 | else:
104 | js_filename = 'moment.min.js'
105 |
106 | if not sri:
107 | mjs = ('\n').format(
109 | version, js_filename)
110 | else:
111 | mjs = ('\n').format(
114 | version, js_filename, sri)
115 | return Markup('{}\n\n'''.format(
116 | mjs, cls.flask_moment_js()))
117 |
118 | @staticmethod
119 | def locale(language='en', auto_detect=False, customization=None):
120 | """Configure the moment.js locale.
121 |
122 | :param language: The language code.
123 | :param auto_detect: If ``True``, detect the locale from the browser.
124 | :param customization: A dictionary with custom options for the locale,
125 | as needed by the moment.js library.
126 | """
127 | if auto_detect:
128 | return Markup('')
132 | if customization:
133 | return Markup(
134 | ''.format(
135 | language, customization))
136 | return Markup(
137 | ''.format(language))
138 |
139 | @staticmethod
140 | def flask_moment_js():
141 | """Return the JavaScript supporting code for this extension.
142 |
143 | This method is provided to enable custom configurations that are not
144 | supported by ``include_moment``. The return value of this method is
145 | a string with raw JavaScript code. This code can be added to your own
146 | ``
151 |
152 | Alternatively, the code can be returned in a JavaScript endpoint that
153 | can be loaded from the HTML file as an external resource::
154 |
155 | @app.route('/flask-moment.js')
156 | def flask_moment_js():
157 | return (moment.flask_moment_js(), 200,
158 | {'Content-Type': 'application/javascript'})
159 |
160 | Note: only the code specific to Flask-Moment is included. When using
161 | this method, you must include the moment.js library separately.
162 | """
163 | default_format = ''
164 | if 'MOMENT_DEFAULT_FORMAT' in current_app.config:
165 | default_format = '\nmoment.defaultFormat = "{}";'.format(
166 | current_app.config['MOMENT_DEFAULT_FORMAT'])
167 | return '''moment.locale("en");{}\n{}'''.format(default_format, js_code)
168 |
169 | @staticmethod
170 | def lang(language):
171 | """Set the language. This is a simpler version of the :func:`locale`
172 | function.
173 |
174 | :param language: The language code to use.
175 | """
176 | return moment.locale(language)
177 |
178 | def __init__(self, timestamp=None, local=False):
179 | if timestamp is None:
180 | timestamp = _naive_now()
181 | self.timestamp = timestamp
182 | self.local = local
183 |
184 | def _timestamp_as_iso_8601(self, timestamp):
185 | if isinstance(timestamp, str):
186 | return timestamp # assume it is already in ISO 8601 format
187 | tz = ''
188 | if not self.local:
189 | tz = 'Z'
190 | return timestamp.strftime('%Y-%m-%dT%H:%M:%S' + tz)
191 |
192 | def _render(self, func, format=None, timestamp2=None, no_suffix=None,
193 | units=None, refresh=False):
194 | t = self._timestamp_as_iso_8601(self.timestamp)
195 | data_values = 'data-function="{}"'.format(func)
196 | if format:
197 | data_values += ' data-format="{}"'.format(format)
198 | if timestamp2:
199 | data_values += ' data-timestamp2="{}"'.format(timestamp2)
200 | if no_suffix:
201 | data_values += ' data-nosuffix="1"'
202 | if units:
203 | data_values += ' data-units="{}"'.format(units)
204 | return Markup(('{}').format(
207 | t, data_values, int(refresh) * 60000, t))
208 |
209 | def format(self, fmt=None, refresh=False):
210 | """Format a moment object with a custom formatting string.
211 |
212 | :param fmt: The formatting specification to use, as documented by the
213 | ``format()`` function frommoment.js.
214 | :param refresh: If set to ``True``, refresh the timestamp at one
215 | minute intervals. If set to ``False``, background
216 | refreshing is disabled. If set to an integer, the
217 | refresh occurs at the indicated interval, given in
218 | minutes.
219 | """
220 | return self._render("format", format=(fmt or ''), refresh=refresh)
221 |
222 | def fromNow(self, no_suffix=False, refresh=False):
223 | """Render the moment object as a relative time.
224 |
225 | This formatting option is often called "time ago", since it renders
226 | the timestamp using friendly text strings such as "2 hours ago" or
227 | "in 3 weeks".
228 |
229 | :param no_suffix: if set to ``True``, the time difference does not
230 | include the suffix (the "ago" or similar).
231 | :param refresh: If set to ``True``, refresh the timestamp at one
232 | minute intervals. If set to ``False``, background
233 | refreshing is disabled. If set to an integer, the
234 | refresh occurs at the indicated interval, given in
235 | minutes.
236 | """
237 | return self._render("fromNow", no_suffix=int(no_suffix),
238 | refresh=refresh)
239 |
240 | def fromTime(self, timestamp, no_suffix=False, refresh=False):
241 | """Render the moment object as a relative time with respect to a
242 | given reference time.
243 |
244 | This function maps to the ``from()`` function from moment.js.
245 |
246 | :param timestamp: The reference ``datetime`` object or ISO 8601 string.
247 | :param no_suffix: if set to ``True``, the time difference does not
248 | include the suffix (the "ago" or similar).
249 | :param refresh: If set to ``True``, refresh the timestamp at one
250 | minute intervals. If set to ``False``, background
251 | refreshing is disabled. If set to an integer, the
252 | refresh occurs at the indicated interval, given in
253 | minutes.
254 | """
255 | return self._render("from", timestamp2=self._timestamp_as_iso_8601(
256 | timestamp), no_suffix=int(no_suffix), refresh=refresh)
257 |
258 | def toNow(self, no_suffix=False, refresh=False):
259 | """Render the moment object as a relative time.
260 |
261 | This function renders as the reverse time interval of ``fromNow()``.
262 |
263 | :param no_suffix: if set to ``True``, the time difference does not
264 | include the suffix (the "ago" or similar).
265 | :param refresh: If set to ``True``, refresh the timestamp at one
266 | minute intervals. If set to ``False``, background
267 | refreshing is disabled. If set to an integer, the
268 | refresh occurs at the indicated interval, given in
269 | minutes.
270 | """
271 | return self._render("toNow", no_suffix=int(no_suffix), refresh=refresh)
272 |
273 | def toTime(self, timestamp, no_suffix=False, refresh=False):
274 | """Render the moment object as a relative time with respect to a
275 | given reference time.
276 |
277 | This function maps to the ``to()`` function from moment.js.
278 |
279 | :param timestamp: The reference ``datetime`` object or ISO 8601 string.
280 | :param no_suffix: if set to ``True``, the time difference does not
281 | include the suffix (the "ago" or similar).
282 | :param refresh: If set to ``True``, refresh the timestamp at one
283 | minute intervals. If set to ``False``, background
284 | refreshing is disabled. If set to an integer, the
285 | refresh occurs at the indicated interval, given in
286 | minutes.
287 | """
288 | return self._render("to", timestamp2=self._timestamp_as_iso_8601(
289 | timestamp), no_suffix=int(no_suffix), refresh=refresh)
290 |
291 | def calendar(self, refresh=False):
292 | """Render the moment object as a relative time, either to current time
293 | or a given reference timestamp.
294 |
295 | This function renders relative time using day references such as
296 | tomorrow, next Sunday, etc.
297 |
298 | :param refresh: If set to ``True``, refresh the timestamp at one
299 | minute intervals. If set to ``False``, background
300 | refreshing is disabled. If set to an integer, the
301 | refresh occurs at the indicated interval, given in
302 | minutes.
303 | """
304 | return self._render("calendar", refresh=refresh)
305 |
306 | def valueOf(self, refresh=False):
307 | """Render the moment object as milliseconds from Unix Epoch.
308 |
309 | :param refresh: If set to ``True``, refresh the timestamp at one
310 | minute intervals. If set to ``False``, background
311 | refreshing is disabled. If set to an integer, the
312 | refresh occurs at the indicated interval, given in
313 | minutes.
314 | """
315 | return self._render("valueOf", refresh=refresh)
316 |
317 | def unix(self, refresh=False):
318 | """Render the moment object as seconds from Unix Epoch.
319 |
320 | :param refresh: If set to ``True``, refresh the timestamp at one
321 | minute intervals. If set to ``False``, background
322 | refreshing is disabled. If set to an integer, the
323 | refresh occurs at the indicated interval, given in
324 | minutes.
325 | """
326 | return self._render("unix", refresh=refresh)
327 |
328 | def diff(self, timestamp, units, refresh=False):
329 | """Render the difference between the moment object and the given
330 | timestamp using the provided units.
331 |
332 | :param timestamp: The reference ``datetime`` object or ISO 8601 string.
333 | :param units: A time unit such as `years`, `months`, `weeks`, `days`,
334 | `hours`, `minutes` or `seconds`.
335 | :param refresh: If set to ``True``, refresh the timestamp at one
336 | minute intervals. If set to ``False``, background
337 | refreshing is disabled. If set to an integer, the
338 | refresh occurs at the indicated interval, given in
339 | minutes.
340 | """
341 | return self._render("diff", timestamp2=self._timestamp_as_iso_8601(
342 | timestamp), units=units, refresh=refresh)
343 |
344 |
345 | class Moment(object):
346 | def __init__(self, app=None):
347 | if app is not None:
348 | self.init_app(app)
349 |
350 | def init_app(self, app):
351 | if not hasattr(app, 'extensions'): # pragma: no cover
352 | app.extensions = {}
353 | app.extensions['moment'] = moment
354 | app.context_processor(self.context_processor)
355 |
356 | @staticmethod
357 | def context_processor():
358 | return {
359 | 'moment': current_app.extensions['moment']
360 | }
361 |
362 | def flask_moment_js(self):
363 | return current_app.extensions['moment'].flask_moment_js()
364 |
365 | def create(self, timestamp=None):
366 | return current_app.extensions['moment'](timestamp)
367 |
--------------------------------------------------------------------------------