Here's the right way to do it: Load resources and set a Baseline before you start recognizing progress. Then recognize progress and update the schedule (delaying incomplete work to after the Status Date) at each periodic "Status Date." At each Status Date, planned %Complete is BCWS/Cost. (BCWS is "budgeted cost of work scheduled.") The actual (performance) %Complete is BCWP/Cost. (BCWP is "budgeted cost of work performed," aka "Earned Value.")
Here's the wrong (but not uncommon) way to do it: Don't load resources; don't set a baseline; and don't update the schedule at the Status Date. Apply formulas to 3 custom fields.
"RolledUpDur" (Duration1): = [Duration]. {Calculate summary rows as Rollup-Sum}.
"ShouldBeDur" (Duration2): = IIf([Current Date]>=[Finish],[Duration],IIf([Current Date]<=[Start],0,(ProjDateDiff([Start],[Current Date])))). {Calculate summary rows as Rollup-Sum}.
"ShouldBe%" (Text1): = [Duration2]/[Duration1]*100 & "%". {Calculate summary rows as Use formula}.
[Alternately, if a Baseline HAS been set, then use Baseline Start, Baseline Finish, and Baseline Duration in the custom duration formulas.]
This could be simpler (i.e. a single custom text field) if one doesn't care about including summary tasks in the mix. In Project, %Complete is simply [Actual Duration]/[Duration], and for summaries it's Ʃ(Actual Durations)/Ʃ(Durations). These custom fields are set up to compute the summary "ShouldBe%" the same way.
Why is this the "wrong way"?
Because
a)Schedules should ALWAYS be updated. Incomplete work is never in the past; instead, whatever you didn't get done last week will pile up in front of you, and what you had planned to do this week will be delayed. And
b)Duration %Complete is simply not a valid measure of the value of the work performed nor of the time expended. Its continued use breeds misunderstanding.