Skip to main content

Convert Gregorian Dates to Persian (Jalali) and Vice Versa in VBA

If you need to convert Gregorian dates to Persian (Jalali) dates in Excel or Access VBA, or convert Persian dates back to Gregorian, this tutorial provides a complete VBA module that you can add to your project.

The two main functions are JalaliCalendar, which converts a Gregorian Date to a Persian date, and GregorianCalendar, which converts a Persian date in YYYYMMDD format to a Gregorian date. The module also includes functions for Persian leap-year detection and calculating the number of days between two Persian dates.

Note: This implementation uses 1 Farvardin 1300 (21 March 1921) as its base date and is designed for Persian dates from the year 1300 onward.

Convert a Gregorian Date to Persian in VBA

Use the JalaliCalendar function to convert a Gregorian date to a Persian date. Its first argument is a VBA Date, while the second argument determines the output format.

Sub TestGregorianToJalali()

    Dim result As Variant

    result = JalaliCalendar(DateSerial(2024, 9, 5), jcNumber)

    MsgBox result

End Sub

The result is:

14030615

In other words, September 5, 2024 is converted to 15 Shahrivar 1403.

To receive a readable string containing the weekday and Persian month name, use jcString:

Sub TestGregorianToJalaliString()

    Dim result As Variant

    result = JalaliCalendar(DateSerial(2024, 9, 5), jcString)

    MsgBox result

End Sub

The English version of the module returns:

Thursday 15 Shahrivar 1403

Convert a Persian (Jalali) Date to Gregorian in VBA

Use the GregorianCalendar function to convert a Persian date to Gregorian. The Persian input date must be supplied as an eight-digit number in YYYYMMDD format.

For example, 15 Shahrivar 1403 is entered as 14030615:

Sub TestJalaliToGregorian()

    Dim gregorianDate As Date

    gregorianDate = GregorianCalendar(14030615, True)

    MsgBox Format(gregorianDate, "yyyy-mm-dd")

End Sub

The result is:

2024-09-05

How to Add the Date Conversion Module to Excel or Access

Copy the complete module provided later in this tutorial and add it to a standard VBA module in your Excel or Access project.

  1. Press Alt + F11 in Excel or Access to open the Visual Basic Editor.
  2. Choose Insert → Module.
  3. Paste the complete module code into the new module.
  4. Save the project. In Excel, use a macro-enabled format such as .xlsm.
  5. You can now call JalaliCalendar, GregorianCalendar, and JCDateDiff from your VBA code.

Function Inputs and Output Formats

JalaliCalendar

The JalaliCalendar function receives a Gregorian VBA Date:

JalaliCalendar(InputDate As Date, Optional returnValueType As jCalendar_returnValueType = jcString)

The optional returnValueType argument determines the format of the result:

  • jcString: readable output such as Thursday 15 Shahrivar 1403.
  • jcNumber: a value in YYYYMMDD format such as 14030615.
  • jcSplitArray: a delimited string containing day, month and year, which can then be converted to an array with VBA’s Split function.

For example:

Dim arrDate() As String

arrDate = Split(JalaliCalendar(Date, jcSplitArray), "!")

' arrDate(0) = day
' arrDate(1) = month
' arrDate(2) = year

GregorianCalendar

The GregorianCalendar function receives the Persian date as a numeric value:

GregorianCalendar(inJalaliDate As Long, jalaliDateToGregorian As Boolean)

To perform a direct Persian-to-Gregorian conversion, pass True as the second argument:

GregorianCalendar(14030615, True)

How the Persian–Gregorian Date Conversion Works

The module uses a known base date: 1 Farvardin 1300 is mapped to 21 March 1921. When converting a Gregorian date to Persian, the module first calculates the number of elapsed days from that base date and then converts those days into a Persian year, month and day.

A separate arithmetic function is used to identify Persian leap years. Leap-year detection is important because it determines whether Esfand contains 29 or 30 days.

Output Type Definitions

The module defines an Enum to control the output format of JalaliCalendar:

Public Enum jCalendar_returnValueType
    jcSplitArray = 1
    jcNumber = 2
    jcString = 3
End Enum

Detecting a Persian Leap Year with JalaliKabise

The JalaliKabise function uses an arithmetic calculation to determine whether the supplied Persian year is a leap year:

Public Function JalaliKabise(Yr As Integer) As Boolean

    calcYear = (Yr + 2346) * 0.24219858156
    calcYear = calcYear - Int(calcYear)

    If calcYear < 0.24219858156 Then
        JalaliKabise = True
    Else
        JalaliKabise = False
    End If

End Function

How JalaliCalendar Converts Gregorian Dates to Persian

The function begins by calculating the number of days between the Gregorian input date and the base date:

JCDaysCount = DateDiff("d", #3/21/1921#, InputDate) + 1
JCYear = 1300

Complete Persian years are then subtracted from the elapsed-day count. The module checks each year to determine whether it contains 365 or 366 days.

Do Until JCDaysCount < 365

    If JalaliKabise(JCYear) = True Then
        D = 366
    Else
        D = 365
    End If

    If JCDaysCount - D > 0 Then
        JCDaysCount = JCDaysCount - D
        JCYear = JCYear + 1
    Else
        Exit Do
    End If

Loop

Once the Persian year has been identified, the remaining days are converted to the appropriate Persian month and day.

How GregorianCalendar Converts Persian Dates to Gregorian

The GregorianCalendar function first extracts the day, month and year from the eight-digit Persian date:

GCJalaliDay = Right(inJalaliDate, 2)
GCJalaliMonth = Mid(inJalaliDate, 5, 2)
GCJalaliYear = Left(inJalaliDate, 4)

The function then validates the month and day values, including the special rules for Esfand in leap and non-leap years.

Finally, the number of elapsed Persian-calendar days is added to the Gregorian base date:

GCcalculateDate = DateAdd("d", GCDaysCount - 1, #3/21/1921#)

Calculate the Difference Between Two Persian Dates

The JCDateDiff function converts both Persian dates to Gregorian dates and then uses VBA’s DateDiff function to calculate the number of days between them.

Public Function JCDateDiff(firstDate As Long, secoundDate As Long)

    JCDateDiff = DateDiff("d", _
        GregorianCalendar(firstDate, True), _
        GregorianCalendar(secoundDate, True), _
        vbSaturday)

End Function

Display Today’s Date in the Persian Calendar

Sub ShowTodayJalali()

    Dim todayJalali As String

    todayJalali = JalaliCalendar(Date, jcString)

    MsgBox "Today: " & todayJalali

End Sub

Complete VBA Module for Persian and Gregorian Date Conversion

Copy the complete code below into a standard VBA module. In this English version, jcString returns English weekday names and transliterated Persian month names. Numeric date conversion works the same way as in the Persian version.

Option Explicit

'==============================================================
' Module purpose:
' Convert Persian (Jalali) dates to Gregorian and vice versa
'
' Persian base date: 01-01-1300
' Gregorian base date: 21-03-1921
' Designed range: Persian dates from year 1300 onward
'==============================================================

Dim calcYear As Variant
Dim D As Integer
Dim JCDaysCount As Long
Dim JCMonth As Integer
Dim JCYear As Integer
Dim JCRemainDays As Integer
Dim bytDayOfWeek As Byte
Dim strFaMonth As String
Dim strFaDay As String

Dim GCYear As Integer
Dim GCDaysCount As Long
Dim GCMonth As Integer
Dim GCYearNow As Integer
Dim GCDayNow As Integer
Dim GCMonthNow As Integer
Dim GCJalaliNow As String
Dim arrGCJalaliNow() As String
Dim GCcalculateDate As Date
Dim GCJalaliDay As Integer
Dim GCJalaliMonth As Integer
Dim GCJalaliYear As Integer

Dim strJCMonth As String
Dim strJCRemainDays As String

Public Enum jCalendar_returnValueType
    jcSplitArray = 1
    jcNumber = 2
    jcString = 3
End Enum


Public Function JalaliKabise(Yr As Integer) As Boolean

    calcYear = (Yr + 2346) * 0.24219858156
    calcYear = calcYear - Int(calcYear)

    If calcYear < 0.24219858156 Then
        JalaliKabise = True
    Else
        JalaliKabise = False
    End If

End Function


'==============================================================
' Convert a Gregorian date to a Persian date.
'
' InputDate:
'   Gregorian VBA Date.
'
' returnValueType:
'   jcString     = readable text
'   jcNumber     = YYYYMMDD
'   jcSplitArray = day!month!year string for use with Split()
'==============================================================

Public Function JalaliCalendar( _
    InputDate As Date, _
    Optional returnValueType As jCalendar_returnValueType = jcString _
) As Variant

    JCDaysCount = DateDiff("d", #3/21/1921#, InputDate) + 1
    JCYear = 1300

    Do Until JCDaysCount < 365

        If JalaliKabise(JCYear) = True Then
            D = 366
        Else
            D = 365
        End If

        If JCDaysCount - D > 0 Then
            JCDaysCount = JCDaysCount - D
            JCYear = JCYear + 1
        Else
            Exit Do
        End If

    Loop

    JCRemainDays = JCDaysCount

    If JCRemainDays <= 31 Then
        JCMonth = 1
    End If

    If JCRemainDays > 31 And JCRemainDays <= 186 Then

        JCMonth = 1

        Do Until JCRemainDays <= 31
            JCRemainDays = JCRemainDays - 31
            JCMonth = JCMonth + 1
        Loop

    End If

    If JCRemainDays > 186 And JCRemainDays <= 336 Then

        JCMonth = 7
        JCRemainDays = JCRemainDays - 186

        Do Until JCRemainDays <= 30
            JCRemainDays = JCRemainDays - 30
            JCMonth = JCMonth + 1
        Loop

    End If

    If JCRemainDays > 336 And JCRemainDays < 365 Then
        JCMonth = 12
        JCRemainDays = JCRemainDays - 336
    End If

    If JCRemainDays = 365 Then
        JCMonth = 12
        JCRemainDays = 29
    End If

    If JCRemainDays = 366 Then

        If JalaliKabise(JCYear) = True Then
            JCMonth = 12
            JCRemainDays = 30
        Else
            JCMonth = 1
            JCRemainDays = 1
            JCYear = JCYear + 1
        End If

    End If

    bytDayOfWeek = Format(InputDate, "w", vbSaturday)

    If returnValueType = jcSplitArray Then
        JalaliCalendar = JCRemainDays & "!" & JCMonth & "!" & JCYear
        Exit Function
    End If

    If returnValueType = jcNumber Then

        If JCMonth < 10 Then
            strJCMonth = 0 & JCMonth
        Else
            strJCMonth = JCMonth
        End If

        If JCRemainDays < 10 Then
            strJCRemainDays = 0 & JCRemainDays
        Else
            strJCRemainDays = JCRemainDays
        End If

        JalaliCalendar = JCYear & strJCMonth & strJCRemainDays
        Exit Function

    End If

    Select Case JCMonth
        Case 1
            strFaMonth = "Farvardin"
        Case 2
            strFaMonth = "Ordibehesht"
        Case 3
            strFaMonth = "Khordad"
        Case 4
            strFaMonth = "Tir"
        Case 5
            strFaMonth = "Mordad"
        Case 6
            strFaMonth = "Shahrivar"
        Case 7
            strFaMonth = "Mehr"
        Case 8
            strFaMonth = "Aban"
        Case 9
            strFaMonth = "Azar"
        Case 10
            strFaMonth = "Dey"
        Case 11
            strFaMonth = "Bahman"
        Case 12
            strFaMonth = "Esfand"
    End Select

    Select Case bytDayOfWeek
        Case 1
            strFaDay = "Saturday"
        Case 2
            strFaDay = "Sunday"
        Case 3
            strFaDay = "Monday"
        Case 4
            strFaDay = "Tuesday"
        Case 5
            strFaDay = "Wednesday"
        Case 6
            strFaDay = "Thursday"
        Case 7
            strFaDay = "Friday"
    End Select

    If returnValueType = jcString Then
        JalaliCalendar = strFaDay & " " & _
                         JCRemainDays & " " & _
                         strFaMonth & " " & _
                         JCYear
    End If

End Function


'==============================================================
' Convert a Persian date to Gregorian.
'
' inJalaliDate:
'   Persian date in YYYYMMDD format.
'   Example: 14030615
'
' For direct Persian-to-Gregorian conversion:
'   jalaliDateToGregorian = True
'==============================================================

Public Function GregorianCalendar( _
    inJalaliDate As Long, _
    jalaliDateToGregorian As Boolean _
) As Variant

    GCJalaliDay = Right(inJalaliDate, 2)
    GCJalaliMonth = Mid(inJalaliDate, 5, 2)
    GCJalaliYear = Left(inJalaliDate, 4)

    ' Basic validation
    If GCJalaliDay > 31 Or GCJalaliMonth > 12 Then
        GregorianCalendar = "False"
        MsgBox "The input date is invalid.", vbCritical
        Exit Function
    End If

    If GCJalaliDay = 0 Or _
       GCJalaliMonth = 0 Or _
       GCJalaliYear < 1300 Then

        GregorianCalendar = "False"
        MsgBox "The input date is invalid.", vbCritical
        Exit Function

    End If

    If GCJalaliMonth > 6 Then

        If GCJalaliDay > 30 Then
            GregorianCalendar = "False"
            MsgBox "The input date is invalid.", vbCritical
            Exit Function
        End If

    End If

    If GCJalaliMonth = 12 Then

        If JalaliKabise(GCJalaliYear) = False Then

            If GCJalaliDay > 29 Then
                GregorianCalendar = "False"
                MsgBox "The number of days in Esfand is invalid.", vbCritical
                Exit Function
            End If

        End If

    End If

    ' Calculate elapsed days from the beginning of year 1300
    GCDaysCount = 0
    GCYear = 1300

    Do Until GCYear = GCJalaliYear

        If JalaliKabise(GCYear) = True Then
            D = 366
        Else
            D = 365
        End If

        GCDaysCount = GCDaysCount + D
        GCYear = GCYear + 1

    Loop

    ' Convert elapsed months to days
    GCMonth = GCJalaliMonth - 1

    If GCMonth <> 0 Then

        Select Case GCMonth
            Case 1
                GCDaysCount = GCDaysCount + 31
            Case 2
                GCDaysCount = GCDaysCount + 62
            Case 3
                GCDaysCount = GCDaysCount + 93
            Case 4
                GCDaysCount = GCDaysCount + 124
            Case 5
                GCDaysCount = GCDaysCount + 155
            Case 6
                GCDaysCount = GCDaysCount + 186
            Case 7
                GCDaysCount = GCDaysCount + 216
            Case 8
                GCDaysCount = GCDaysCount + 246
            Case 9
                GCDaysCount = GCDaysCount + 276
            Case 10
                GCDaysCount = GCDaysCount + 306
            Case 11
                GCDaysCount = GCDaysCount + 336
        End Select

    End If

    GCDaysCount = GCDaysCount + GCJalaliDay

    GCcalculateDate = DateAdd( _
        "d", _
        GCDaysCount - 1, _
        #3/21/1921# _
    )

    If jalaliDateToGregorian = True Then
        GregorianCalendar = GCcalculateDate
        Exit Function
    End If

    ' Convert the current date to Persian
    GCJalaliNow = JalaliCalendar(Now, jcSplitArray)
    arrGCJalaliNow = Split(GCJalaliNow, "!")

    GCDayNow = arrGCJalaliNow(0)
    GCMonthNow = arrGCJalaliNow(1)
    GCYearNow = arrGCJalaliNow(2)

    ' Reject future dates in this mode
    If GCYearNow < GCJalaliYear Then
        MsgBox "A future date cannot be entered.", vbCritical
        GregorianCalendar = "False"
        Exit Function
    End If

    If GCYearNow = GCJalaliYear Then

        If GCMonthNow < GCJalaliMonth Then
            MsgBox "A future date cannot be entered.", vbCritical
            GregorianCalendar = "False"
            Exit Function
        End If

    End If

    If GCYearNow = GCJalaliYear Then

        If GCMonthNow = GCJalaliMonth Then

            If GCDayNow < GCJalaliDay Then
                MsgBox "A future date cannot be entered.", vbCritical
                GregorianCalendar = "False"
                Exit Function
            End If

        End If

    End If

    GregorianCalendar = JalaliCalendar( _
        GCcalculateDate, _
        jcString _
    )

End Function


'==============================================================
' Calculate the number of days between two Persian dates
'==============================================================

Public Function JCDateDiff( _
    firstDate As Long, _
    secoundDate As Long _
)

    JCDateDiff = DateDiff( _
        "d", _
        GregorianCalendar(firstDate, True), _
        GregorianCalendar(secoundDate, True), _
        vbSaturday _
    )

End Function

Video Tutorial: Using the Module in Excel

The following video demonstrates how to transfer the module to Excel and use its Persian–Gregorian date conversion functions.

Step-by-step tutorial for adding the Persian–Gregorian date conversion module to Excel and using its VBA functions.

Frequently Asked Questions

How do I convert a Gregorian date to Persian in VBA?

Pass a VBA Date value to JalaliCalendar. For example, JalaliCalendar(Date, jcNumber) converts today’s system date to a Persian date in YYYYMMDD format.

What format should I use for a Persian input date?

GregorianCalendar expects an eight-digit Persian date in YYYYMMDD format. For example, 15 Shahrivar 1403 is entered as 14030615.

Does this VBA code work in both Excel and Access?

Yes. The module can be added to a standard VBA module in both Microsoft Excel and Microsoft Access projects.

Does jcSplitArray return an actual array?

No. Despite its name, this mode returns a day!month!year string. Use VBA’s Split function to convert that string into an array.

Does this module support Persian dates before 1300?

This implementation is based on 1 Farvardin 1300 and the Persian-to-Gregorian function treats years earlier than 1300 as invalid.

Summary

This tutorial provides a complete VBA solution for converting Gregorian dates to Persian (Jalali) dates and Persian dates back to Gregorian. JalaliCalendar performs Gregorian-to-Persian conversion, while GregorianCalendar converts a Persian YYYYMMDD value to a Gregorian VBA date.

The module also includes Persian leap-year detection and the JCDateDiff function for calculating the number of days between two Persian dates, making it a useful base for Persian date handling in Excel and Access VBA projects.

Leave a Reply

Your email address will not be published. Required fields are marked *

تأیید امنیتی هنگام تعامل با فرم بارگذاری می‌شود.