Open-source project
omid/Persian-Calendar-for-Gnome-Shell avatar
omid/Persian-Calendar-for-Gnome-Shell

Persian Calendar for GNOME Shell: putting a Jalali date in the top bar

A GNOME Shell extension that displays Persian calendar

561 stars33 forksJavaScriptGPL-3.0

At a glance

What is it?
The Persian Calendar extension adds Persian, Gregorian and Hijri dates to the GNOME Shell top bar, with configurable formats, event and holiday support, RTL handling and accessibility features. It is on the official GNOME Shell Extensions site, and the Makefile shows the release process it uses, from linting through version bump to the packed zip.
Who is it for?
Adopt this extension if you work with Persian dates and want them visible in the GNOME top bar without converting every time you look, since it shows the Jalali, Gregorian and Hijri calendars side by side. Do not adopt it if you only need the calendar occasionally, because the system calendar already covers the Gregorian case.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the extension does

The Persian Calendar for GNOME Shell is a GNOME Shell extension that displays Persian, Iranian and Jalali dates alongside Gregorian and Hijri, lunar, dates in the top bar.

Three calendars in one indicator is more than a date formatter. A Persian-speaking user working across systems needs to think in the Jalali calendar but has to deal with Gregorian dates in every form, invoice and software tool outside the shell, and the Hijri date appears in religious observance, official documents and regional business practice. Putting all three in the panel means the conversion is always visible rather than something you perform in your head or in a search box.

The feature list is six items: display Persian, Gregorian and Hijri dates, support for multiple calendar formats, customizable appearance, event and holiday support, RTL language support and accessibility features.

Two of those deserve comment. Event and holiday support means the extension can mark days in the panel that are not ordinary days, which is the kind of thing that has to come from a locale-aware source rather than being hardcoded, since the holiday calendar differs between countries that share the same calendar system. And RTL language support is listed as a feature rather than assumed, which for a calendar extension in a right-to-left language environment is not a small detail, since the layout of the panel indicator and the ordering of the date fields both have to be right.

The accessibility entry is also worth noting. GNOME extensions are a common source of accessibility complaints, because a custom panel indicator that is not properly exposed to the accessibility bus is invisible to a screen reader. Listing it as a feature suggests the author was aware of that.

The project is JavaScript, GPL-3.0, published on the official GNOME Shell Extensions site as extension 240, and the last push was on 2026-10-19. It took part in Hacktoberfest, which the repository topics record, and the topics list the full spread of calendar vocabulary, including gregorian-calendar, hijri-calendar, hijri-shamsi, jalali-calendar and lunar-calendar.

Installing it, and the two common failures

The recommended installation is the official website. Go to the GNOME Shell Extensions page, click the ON/OFF button in the top-right of the page, and if the extension does not appear, go to the troubleshooting section.

The fact that the troubleshooting section is linked from the installation step itself is a good sign, because the two failure modes are well known and both have a specific cause.

The first is that you do not see the ON/OFF button, or it is greyed out, and instead a blue message appears at the top of the site saying that to control GNOME Shell extensions using this site you must install GNOME Shell integration. The fix is to install the gnome-browser-connector package on your system, and the readme gives the command for Ubuntu, apt install gnome-browser-connector.

That message comes from the browser rather than from the extension. The website needs a browser extension to talk to the shell, and without it the page cannot enable anything, which is why the button is missing rather than merely failing.

The second failure is the more confusing one. You enable the extension, it does not appear, and when you refresh the page it is disabled again. The readme's answer is to run the gnome-extensions application and make sure extensions are enabled and the toggle in front of the extension is turned on as well.

That is a two-level switch. The website controls what the extension manager will install, and the local Extensions application controls what the shell actually loads. If the local application has the extension turned off, the website cannot see it and the toggle does not stick.

For developers, the readme gives a separate path: clone the repository, go into the cloned directory, run make in the terminal, restart GNOME Shell with ALT+F2 then r then Enter, or log out and back in, and enable the extension through the Extensions application if needed.

Date formatting and the format codes

The readme documents the format codes the extension supports, and the design is worth understanding because it is a strftime-like scheme with a Persian-specific twist.

There are two families of code. The upper case ones produce long forms and the lower case ones produce short or numeric forms, and the distinction is per-field rather than global.

For the year, %Y is the year with century and %y is the year without century, zero-padded. For the month, %MM is the month name and %mm is the month name shortened, while %M is the month number zero-padded and %m is the month number without padding. So the upper case pair is the long name and the lower case pair is either a short name or a plain number, and the numeric ones are the ones that keep the Persian digits.

For the day of the month, %D is zero-padded and %d is not. For the day of the week there are three levels: %WW is the full day name, %ww is the short form and %w is a single character.

The important property for a Persian calendar is that the numeric output uses Persian digits rather than Latin ones. The example output column in the readme shows the year as Persian digits, the month number as Persian digits, and the day likewise, while the day-of-week codes produce Persian script names.

That is not a cosmetic detail. A Jalali date written with Latin digits is not what an Iranian user expects to read, and in a shell top bar where the font fallback and the digit shapes matter, mixing the two scripts is immediately visible as wrong.

The three day-of-week lengths are also a genuine need rather than a copy of strftime. Persian day names are words, and the single character form exists because the top bar indicator has very little horizontal space, so a one character abbreviation is sometimes the only thing that fits.

The extension is configured through GNOME's own settings mechanism, with a schema file, and the Makefile shows that the schema is compiled at install time with glib-compile-schemas. The build directory is named in the packaging command as the schema path, so the schema travels with the extension.

Repository layout and packaging

The top level of the repository is small and tells you how a GNOME extension is expected to be arranged. The source lives in a directory named after the extension UUID, [email protected], which is the convention GNOME expects because the directory name has to match the UUID in the metadata file. There is a Makefile, an assets/ directory holding the screenshot and the screencast, an archlinux/ directory, a lint/ directory, and an eslint.config.mjs at the root.

The assets hold both a screenshot image and a screencast as a webp file. A screencast is the more useful of the two for an extension, because it shows the panel indicator changing over time, which is the only way to demonstrate a date display whose whole purpose is to be read at a glance.

The archlinux/ directory is a distribution package recipe, and its presence connects back to the DESTDIR handling in the Makefile. A packager builds the extension and installs it to the system-wide location rather than the per-user one, and that is exactly what the conditional in the install target is for. It is a small piece of infrastructure that means the extension can be packaged properly for a distribution rather than only sideloaded.

The lint/ directory sits alongside an eslint.config.mjs and a package.json whose only content is devDependencies. There is no runtime dependency at all: the eslint packages and the eslint-plugin-jsdoc are the only things listed. That is a small signal about the extension itself, since a GNOME Shell extension runs inside GJS, the JavaScript engine bundled with the shell, and the calendar arithmetic is implemented against what that engine provides rather than against a date library you would install.

The licensing is GPL-3.0 with a LICENSE.md in the repository, and the readme links the project on GitHub, the extension listing on extensions.gnome.org, and a PayPal link for the author.

The Makefile and how a release is cut

The Makefile is where most of the practical knowledge about this project lives, and it is a well-organised one.

The extension UUID is [email protected], and the source lives in a directory with that name. The install target works out where to put it. With DESTDIR empty, the install base is $HOME/.local/share/gnome-shell/extensions, which is the per-user location. With DESTDIR set, it is $(DESTDIR)/usr/share/gnome-shell/extensions, which is the system-wide location a distribution package would use.

That conditional is the difference between a developer installing locally and a packager building a distribution package, and it is why there is an archlinux/ directory in the repository.

The install-local target depends on the build, compiles the schemas, removes any previous copy of the extension, creates the directory and copies the built output into place, then cleans up. There is a comment explaining why the schema compilation is needed: it is only needed for manual installs, because the extension manager compiles schemas itself. That is a reference to the GNOME extension review guidelines, which advise against shipping unnecessary files in the extension.

The release target is a chain of dependencies, and the order is the interesting part. It depends on export, then eslint, then shexli, then _version_bump. Then it runs gitg, which is a git commit helper with a GUI, commits with a verbose message, tags the result with the version, pushes the branch and pushes the tags, and finally makes the zip.

The version comes out of the metadata.json file with jq, and the bump target reads the old version, computes the new one by adding one in shell arithmetic, and rewrites the version field in metadata.json with sed. So the version number lives in one place, the GNOME metadata file, and the release process reads it rather than duplicating it.

The zip target then calls gnome-extensions pack with --force, and the command is careful about what goes in. It passes everything except the files that gnome-extensions includes by itself, filtering the build directory listing with grep to exclude extension.js, prefs.js, metadata.json, stylesheet.css and schemas, and turning what remains into --extra-source arguments. The schema is passed separately with --schema. The result is renamed to include the UUID and the version.

The lint step is npx eslint --fix against the extension directory, and the eslint.config.mjs at the repository root with eslint-plugin-jsdoc in the dev dependencies tells you the project runs a modern flat ESLint config with documentation rules enforced.

Editorial conclusion

Adopt this extension if you work with Persian dates and want them visible in the GNOME top bar without converting every time you look, since it shows the Jalali, Gregorian and Hijri calendars side by side. Do not adopt it if you only need the calendar occasionally, because the system calendar already covers the Gregorian case. Before you commit, install the gnome-browser-connector package if the extensions website refuses to toggle the extension, and note that the extension must be enabled in the local Extensions application as well, which is the second half of the troubleshooting the readme describes.

Frequently asked questions

What does the Persian Calendar for GNOME Shell extension do?

It is a GNOME Shell extension that displays Persian, Iranian and Jalali dates alongside Gregorian and Hijri lunar dates in the top bar. It also supports multiple calendar formats, customizable appearance, event and holiday support, RTL language support and accessibility features.

How do I install the Persian Calendar extension?

Go to the official GNOME Shell Extensions website for extension 240 and click the ON/OFF button in the top-right of the page. If the extension does not appear, the readme points you at its troubleshooting section, which covers the browser connector requirement and the local Extensions application toggle.

Why can I not turn the extension on from the GNOME Shell Extensions website?

If you see a blue message saying you must install GNOME Shell integration, the website needs the browser connector. Install the gnome-browser-connector package, for example with apt install gnome-browser-connector on Ubuntu. If the extension enables and then disables itself on refresh, run the gnome-extensions application and turn on both the extensions toggle and the toggle in front of the extension itself.

Which date format codes does the extension support?

For the year, %Y is the year with century and %y is the year without. For the month, %MM is the month name, %mm is the short month name, %M is the zero-padded month number and %m is the month number. For the day, %D is zero-padded and %d is not. For the day of the week, %WW is the full name, %ww is short and %w is a single character.

How is a release of the Persian Calendar extension built?

The Makefile drives it. The release target runs eslint, then shexli, then bumps the version in the extension's metadata.json by reading it with jq and rewriting it with sed, then commits, tags with that version, pushes the branch and the tags, and finally builds a zip with gnome-extensions pack, excluding the files that tool includes itself and passing the schema separately.

Official sources

  1. Issues
  2. License: GPL-3.0
  3. omid/Persian-Calendar-for-Gnome-Shell on GitHub
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/omid-persian-calendar-for-gnome-shell.svg)](https://hysenlabs.com/projects/omid-persian-calendar-for-gnome-shell)

Was this useful?