Timeyard

Date math حساب تاریخ

Shamsi dates in Excel and Sheets (and the formula that gets 29% wrong)

On this pageدر این صفحه

You have a column of Persian dates and you need real Gregorian ones you can sort, filter and subtract. Two things go wrong, and the second is much worse than the first, because it does not look like anything went wrong at all.

First: the format is a costume, not a conversion

Microsoft documents this plainly. From the YEAR function reference:

“Values returned by the YEAR, MONTH and DAY functions will be Gregorian values regardless of the display format for the supplied date value.”

So a cell showing 1403/01/15 may be storing serial number 45385 and still answer 2024 to =YEAR(A1). Users hit this in the wild — one Microsoft community thread reports exactly it: “for the Hijri date it returns the Gregorian values even though the date is converted using the cell format.”

Formatting is a display layer. If you need the Gregorian date as data, you have to compute it.

Second: the formula everyone copies is wrong, just not where you’d look

This formula circulates widely and was the accepted answer on Microsoft Q&A question 1188161 (asked 9 March 2023, answered the next day):

Don't use this one
=LET(y,VALUE(LEFT(A1,4)), m,VALUE(MID(A1,6,2)), d,VALUE(RIGHT(A1,2)),
  full_months, IF((m-1)<7, (m-1)*31, (m-1)*30+6),
  total,       (y-1)*365 + full_months + d + INT((y-1)/4),
  IF(MOD(y,4)=0, total+1, total) - 466710)
Accepted answer on Microsoft Q&A 1188161. Correct for Jalali 1380–1399 only.

The same arithmetic was already circulating in a December 2017 Microsoft forum thread, pasted by someone asking for help rather than supplied by Microsoft.

Here is why it survived review. It is exactly right for Jalali 1380–1399 — Gregorian 2001 to 2021. Every date anyone would naturally spot-check falls in that window. Test it on today, on last year, on a recent invoice, and it agrees every time.

Step outside those two decades and it drifts:

Jalali yearsGregorianWrongDirection
1340–13491961–197120.0%a day early
1370–13791991–200110.0%a day early
1380–13892001–20110%—
1390–13992011–20210%—
1400–14092021–203110.0%a day late
1410–14192031–204120.0%a day late
1440–14492061–207160.0%a day late

Try a year from your own data — recent dates prove nothing

The half that matters now, not in 2029

Most write-ups of this bug lead with the future. That buries the real problem.

Across Jalali 1300–1450, the failures split 10,950 a day early and 5,121 a day late. The early ones are the older years — and older years are where birth dates live. Iranian passports, birth certificates, old contracts and personnel records are full of 13xx dates from the 1340s to 1370s.

An HR clerk converting a 1351 birth date with that formula gets the wrong day today, and nothing in the spreadsheet flags it. The 1408 breakage — the year the drift flips direction, in 2029 — is real but secondary.

Why it drifts: Microsoft’s Excel answer contradicts Microsoft’s own docs

The formula’s leap rule is MOD(y,4)=0. The real rule is not four-yearly. Microsoft’s own .NET PersianCalendar documentation states it:

“A leap year is a year that, when divided by 33, has a remainder of 1, 5, 9, 13, 17, 22, 26, or 30.”

Eight leap years in 33, not eight in 32. Look at the set and you can see the trap:

Jalali yearmod 33Leap?Gap
139913leap4
140317leap4
140822leap5 ←
141226leap4
141630leap4

Between 1403 and 1408 there is a five-year gap. Every formula built on “every 4th year” must eventually slip a day there, and the -466710 constant only papers over it while the running leap count happens to be off by exactly 10 — which is true for 1380–1399 and not for long.

A pure formula that is exact

No Apps Script, no VBA, no Power Query, no array constants. Paste into one cell and format the result as a date:

Excel · Google Sheets
=LET(t,     SUBSTITUTE(TRIM(A1),"/","-"),
   y,     VALUE(LEFT(t,4)),
   m,     VALUE(MID(t,6,2)),
   d,     VALUE(RIGHT(t,2)),
   p,     y-1,
   r,     MOD(p,33),
   leaps, 8*INT(p/33) + IF(r<18, INT((r+3)/4), INT((r+2)/4)),
   doy,   IF(m<8, (m-1)*31, (m-1)*30+6) + d,
   365*p + leaps + doy - 466700)
Paste into one cell with the Shamsi date in A1, then format the result cell as a date.

Each binding does one job:

NameWhat it holdsWhy
tthe date text, slashes normalised to dashesaccepts 1403/01/15 or 1403-01-15
y m dthe three numberspulled from fixed positions, so the year must be 4 digits
py - 1 — complete years before this onea leap day belongs to years after the leap year
rp mod 33position in the 33-year leap cycle
leapsleap days elapsedclosed form for “how many of 30 are ≤ r”
doyday of the Persian yearfirst 6 months have 31 days, the rest 30
resultExcel serial number365 × years + leap days + day-of-year − epoch

Checked against a calendar engine over Jalali 1178–1501 (Gregorian 1799–2123): 0 wrong out of 118,338 dates. Restricted to what Excel can represent — 1 March 1900 onward — it is 0 wrong out of 81,449.

Stated limits, because any arithmetic Persian formula has them:

It works the same in Google Sheets, which supports LET.

A trap worth more than the formula

That 2017 thread shows a failure mode no formula fixes. A user pasted Shamsi dates like 1396/04/03 from an Iranian billing site, and the copy dropped the leading digits to 96/04/03. Excel happily read that as a Gregorian date in 1996 and stored serial 35128 — 4 March 1996, about 21 years from the intended 24 June 2017.

The cell then displayed under a Persian-looking number format, so it looked converted. Before trusting any conversion, widen the column and check that your source text still has four digits in the year.

For one-off dates, skip the spreadsheet: use the converter directly. The leap rule behind all of this is worked through in Persian leap years explained, and the year-boundary problem in Shamsi to Gregorian conversion.

Frequently asked questions

  • Why does YEAR() return a Gregorian year after I format the cell as a Persian date?

    Because the format only changes the display. Microsoft documents that YEAR, MONTH and DAY return Gregorian values regardless of the display format. The underlying value is a serial number, so you have to convert it rather than reformat it.

  • Is the popular Shamsi-to-Gregorian LET formula wrong?

    It is correct for Jalali 1380 to 1399, which is why it is widely accepted, and a day out either side of that. Tested over Jalali 1300 to 1450 it is wrong for 29.1% of dates — a day early for most older years and a day late from 1408 onward.

  • Why do four-year leap formulas fail on the Persian calendar?

    The Persian leap rule distributes 8 leap years across a 33-year cycle, not one every four years. There is a five-year gap between the leap years 1403 and 1408, and any every-4th-year formula slips a day there.

  • Does the corrected formula work in Google Sheets?

    Yes. It uses only LET, MOD, INT, VALUE and text functions, all of which Sheets supports. No Apps Script is needed.

  • What range is the corrected formula valid for?

    Jalali 1279 to 1501, roughly Gregorian 1900 to 2123. It was checked against a calendar engine with zero errors across 118,338 dates in that band. Beyond it, arithmetic implementations of the Persian calendar start to disagree with one another.

Open the converter بازگشت به مبدّل