Next Friday's Date

I am not happy with KDE 6.5.0. And, so, as an openSUSE Tumbleweed user, I've switched to the included IceWM instead.

I'm accustomed to seeing the calendar widget when I click on the clock in KDE. But, in IceWM, clicking there just opens a bigger clock application, which is very 90's.

But, I'd like to know the date for next Friday. That's my goal.

I am proficient in PowerShell. So, I used PowerShell to display next Friday's date:

0..6 | 
  ForEach-Object { (Get-Date).addDays($_) } | 
  Where-Object { $_.DayOfWeek -eq [DayOfWeek]::Friday } | 
  ForEach-Object { $_.ToLongDateString() }  

# which outputs...

Friday, November 7, 2025

Note that this output is locale specific. The date format will automatically change depending on where the computer is located. E.g., 12/31/99 versus 31/12/99.

Explanation

Let me go through the PowerShell one-liner step by step.

Outputting a Range of Dates

0..6 is a range which outputs the numbers 0, 1, 2, 3, 4, 5, and 6.

The pipe | starts a PowerShell pipeline.

This connects the output from one command to the input of the next command. This pipeline will run once for each integer passed down the pipeline, e.g., 0, 1, 2 ...

ForEach-Object is a cmdlet that allows you to execute a command for each object passed down the pipeline. So, I can put some code here. And, it will run once for each integer in the range.

We put our code inside curly braces { code here } Curly braces are called script blocks in PowerShell.

We refer to the current value of the pipeline with an automatic variable $_ This represents the current value traveling down the pipeline. So, if 1 was emitted from the range during this cycle, then $_ would represent 1 inside ForEach-Object's curly braces. (Actually, $_ represents the current value anywhere it can be evaluated within the pipeline such as in tests or cmdlet parameter values.)

Get-Date is the PowerShell cmdlet that works with dates. By default, it returns the current date.

The parentheses ( ) around Get-Date allow us to use methods on the output (e.g., [DateTime] object) from Get-Date. The parentheses are known as the grouping operator in PowerShell.

The [DateTime] method addDays() allows us to add a given number of days to the current date.

The dot . between Get-Date and addDays() has its own name: in PowerShell, this use of "." is called the member-access operator.

So, let's take a look at what we have so far:

0..6 | 
  ForEach-Object { (Get-Date).addDays($_) }

# which outputs...

Sunday, November 2, 2025 1:01:58 AM
Monday, November 3, 2025 1:01:58 AM
Tuesday, November 4, 2025 1:01:58 AM
Wednesday, November 5, 2025 1:01:58 AM
Thursday, November 6, 2025 1:01:58 AM
Friday, November 7, 2025 1:01:58 AM
Saturday, November 8, 2025 1:01:58 AM

In other words, this first part of the one-liner outputs a range of dates.

We use the numbers 0-6 as inputs into the pipeline. Then, I return a date for each integer by using the addDays() method and the integer. addDays() adds the specified number of days. And, the resulting output is each day of the following week.

There are seven days in a week and seven numbers output by the range 0..6 Starting with 0 here covers the case that today is Friday. But, generally, the next Friday will be 1-6 days out.

So, we've generated a list of candidates. And, Friday is in there somewhere. But, we have not selected a specific day.

Since we're continuing this pipeline, we place a pipe | at the end of each line. PowerShell interprets this both as another segment of the pipeline, and a line-continuation character. That means, even though the commands appear on different lines, the entire pipeline is interpreted as one command (one-liner).

Inspecting the Pipeline

One important thing to notice is that the values flowing down the pipeline have changed.

At first, we started our pipeline with a range 0..6 which output integers. And those integers were the things traveling down, and driving, the pipeline.

But, after using Get-Date, we're now outputting date objects (specifically, .NET [DateTime] objects). After the Get-Date pipeline segment, the [DateTime] objects are what is traveling down the pipeline. So, the integers are gone and these date objects are what we manipulate from now on.

When working with pipelines, make sure to note when the type of object changes and where in the pipeline that happens. In this case, we know that the Get-Date cmdlet outputs [DateTime] objects. So, we'll expect a change from integers to date objects at that point in the pipeline.

As a further sidebar, if you did not know the type of object being emitted by the pipeline at a given point, then you can use the Get-Member cmdlet to discover the type of output. Use the word "type" or "class" when referring to the general concept of data types like [Int] or [DateTime] or [String]

To do it, take the pipeline and chop off the end. Then, add a new segment and the Get-Member cmdlet.

Let's look at a quick example:

0..6 | 
  ForEach-Object { (Get-Date).addDays($_) } | 
  Get-Member

# which outputs...

   TypeName: System.DateTime

Name                 MemberType     Definition
----                 ----------     ----------
Add                  Method         datetime Add(timespan value)
AddDays              Method         datetime AddDays(double value)
AddHours             Method         datetime AddHours(double value)

# ... and many other methods and properties. 

By adding Get-Member to a pipeline segment immediately after the part that we're interested in, we get critical information about what is being output. In this case, we see that the "TypeName" is "System.DateTime". That can be shortened to "DateTime", or [DateTime] as I've been styling it. We can also see the AddDays method listed in the Get-Member output.

Get-Member is an important discovery tool. Use the Get-Member cmdlet while you are developing a pipeline to learn more about unfamiliar output or types.

Finding Friday

The Where-Object cmdlet filters objects passing down the pipeline.

To get it to work, we write an expression that will return TRUE for objects that we want to pass further down the pipeline.

Where-Object { $_.DayOfWeek -eq [DayOfWeek]::Friday }

Generally, we'll compare some aspect of the current pipeline value (such as $_), using a comparison operator (such as -eq), to some value (such as Friday). All of this appears in a script block { } If that script block evaluates to TRUE then that particular pipeline object is passed thru the Where-Object segment to the next pipeline segment.

If you are familiar with SQL WHERE clauses, then you might want to read the Where-Object documentation (linked below). Note there is an alternative syntax available which is very similar to SQL WHERE. But, I learned the original PowerShell way to write conditional clauses. And, that is the most compatible style, e.g., it works with PowerShell 2.0.

I'll skip over the parts I covered previously like script blocks { }, the current pipeline object $_, and the membership-access operator "." (dot)

The most confusing parts of the Where-Object segment are the two different uses of DayOfWeek, the comparison operator -eq, and I'll end by reading the whole thing.

Where-Object { $_.DayOfWeek -eq [DayOfWeek]::Friday }

DayOfWeek is used in two ways in this example.

One DayOfWeek is used after $_ as in $_.DayOfWeek

The other DayOfWeek is used in square brackets, that is [DayOfWeek]::Friday

The confusion stems from using the same name, DayOfWeek, in two unrelated contexts. These are not referring to the same variable.

$_.DayOfWeek is a property. It is a property of a [DateTime] object. This is the current day for each object passing down the pipeline.

[DayOfWeek]::Friday is a .NET enumeration. Specifically, "[DayOfWeek]" is the enumeration. "::" is the static member operator. And "Friday" is a member of the enumeration. This is a static value that we'll be comparing to each object passing down the pipeline.

There is some nuance here. But, in general, we use the static member operator :: when accessing a constant in an enumeration, or when accessing the static methods or properties of a *class* itself and not an instance of a class (aka object). Static information can be stored in enumerations and classes. So, in this case, we're accessing that static (unchanging) information.

So, if I was looking at the documentation and I wanted to access a static member of a class or struct then I would write:

[Class or Struct]::Method_Property_or_NamedConstant

such as

[DayOfWeek]::Friday
[Math]::Pi
or
[Convert]::ToBase64String([Byte[]] [Char[]] "Hello" )

>

(Note: I'm typecasting my string to an array of char and then array of bytes: which is what ToBase64String() accepts).

Returning to the pipeline, to further confuse things, the property is of type [DayOfWeek], e.g.:

0..6 | 
  ForEach-Object { (Get-Date).addDays($_) } | 
  ForEach-Object { $_.DayOfWeek } | 
  Get-Member 

# which outputs...

   TypeName: System.DayOfWeek

Name        MemberType Definition
----        ---------- ----------
CompareTo   Method     int CompareTo(System.Object target), int IComparable.Co…
Equals      Method     bool Equals(System.Object obj)
GetHashCode Method     int GetHashCode()
...

This will make more sense as you work with PowerShell more. These distinctions are not important now.

Where-Object { $_.DayOfWeek -eq [DayOfWeek]::Friday }

The comparison is relatively straightforward if you have any programming experience.

A comparison operator, like -eq, appears in the middle.

Then, the values to the left and right of the operator are compared. That is "$_.DayOfWeek" (the current day of the current pipeline object) is compared with "[DayOfWeek]::Friday" (a static reference to the 'Friday' constant).

If it was not obvious, -eq stands for "equals" and, in PowerShell, the underlying values must be equal for this comparison operator to return a TRUE value. Otherwise, it returns FALSE.

Note, these values are subject to type coercion. E.g.:

[int] 3 -eq [double] 3.0

True

And, I'm sorry to say but I actually wrote my clause backward.

I wrote:

Where-Object { $_.DayOfWeek        -eq [DayOfWeek]::Friday }
Where-Object { [DayOfWeek]::Friday -eq $_.DayOfWeek        }

But, by convention, I should have reversed the operands per the second example above.

If you notice, there is a left-hand side and a right-hand side to the comparison operator.

If a variable appears on the left-hand side and it is a *collection* (a special PowerShell array), then PowerShell will automatically expand the collection and enumerate each value, but, more importantly, this variable expansion will fundamentally change the way the comparison works, and **that is likely to change the result**. That is, you might be expecting TRUE and get FALSE instead, simply because the left-hand side was a collection. This behavior is working as intended and makes sense for the original use cases. But, it is confusing and not obvious.

In the PowerShell community, the convention has been to place the variable, e.g., $_.DayOfWeek, on the right-hand side and the static value on the left-hand side, e.g., [DayOfWeek]::Friday. This forces the obvious interpretation of the comparison.

Watch for these left/right mix-ups in comparisons. They can be a confusing source of error.

If you visit the about_comparison_operators PowerShell help article, the section on "Common Features" explains this issue in the third paragraph:

When the left-hand value in the comparison expression is a scalar value, the operator returns a Boolean value. When the left-hand value in the expression is a collection, the operator returns the elements of the collection that match the right-hand value of the expression. Right-hand values are always treated as singleton instances, even when they're collections.

Plus more esoteric details.

Read that section of the help file once you level up your PowerShell skills.

But, my ad hoc version of the comparison works. And, I'll stick with that.

Where-Object { $_.DayOfWeek -eq [DayOfWeek]::Friday } | 

Let's read this whole line.

We have a Where-Object cmdlet:

Where-Object { }

We have the left-hand value of the current pipeline object:

$_.DayOfWeek

We have the comparison operator we chose. There are other operators we could have used. Those are helpful in different circumstances.

-eq

We have the right-hand value. This is a constant which represents the day of the week "Friday".

[DayOfWeek]::Friday

Finally, we add a pipe | to the end to continue the pipeline to the next line:

|

So, when each pipeline object is evaluated by this expression, we'll be doing comparisons like:

Where-Object { Saturday  -eq Friday } 
Where-Object { Sunday    -eq Friday } 
Where-Object { Monday    -eq Friday } 
Where-Object { Tuesday   -eq Friday } 
Where-Object { Wednesday -eq Friday } 
Where-Object { Thursday  -eq Friday } 
Where-Object { Friday    -eq Friday } 

I hope that you realize that most of these comparisons will return FALSE. And, so, those objects will be blocked by Where-Object. This is the filtering in action.

But, the Friday to Friday comparison will return TRUE.

And, so, the object which has the $_.DayOfWeek property -equal to ::Friday will be passed to the next part of the pipeline. This makes sense because we are looking for Friday among the candidates.

Remember, every date object has both a DayOfWeek property (e.g., Thursday) and a date (e.g., January 1, 1970). So, once we find the right day (Friday), we can simply output that object's date to achieve my goal.

We can't predict where Friday will appear in the list of candidates. So, we need a way to test each one. The Where-Object cmdlet is performing that filtering function for us here.

Output

The output is simple.

ForEach-Object { $_.ToLongDateString() }  

We use another ForEach-Object cmdlet to execute arbitrary code within the pipeline.

We use the current pipeline value $_ If you were to test its output with Get-Member, you would find that this is still a [DateTime] object.

My personal preference is to simply see the date written out.

[DateTime] objects have a "ToLongDateString" method (which is shown in the full Get-Member output for [DateTime]'s).

So, I just tack a ToLongDateString() method to the current pipeline object.

Remember that, at this point, there is only one possible object. We started by generating a list of seven days. Then, we asked Where-Object to filter out all the non-Fridays. That just leaves one object left, which is what we're implicitly operating on in this last line.

This is the part of the pipeline that outputs. So, we end the pipeline here. Note there is no trailing pipe | after the end of the script block { }.

Altogether, that outputs the date (with the default time component trimmed out):

0..6 | 
  ForEach-Object { (Get-Date).addDays($_) } | 
  Where-Object { $_.DayOfWeek -eq [DayOfWeek]::Friday } | 
  ForEach-Object { $_.ToLongDateString() }  

Friday, November 7, 2025

References

Outputting a Range of Dates

Number Ranges | Microsoft Learn
about_Pipelines | Microsoft Learn
ForEach-Object | Microsoft Learn
Curly Braces (Script Blocks) | Microsoft Learn
$_ | Automatic Variables | Microsoft Learn
Get-Date | Microsoft Learn
( ) Grouping Operator | Microsoft Learn
DateTime.AddDays Method | Microsoft Learn
. (dot) Member-access Operator | Microsoft Learn

Inspecting the Pipeline

Get-Member | Microsoft Learn

Finding Friday

Where-Object | Microsoft Learn
about_Comparison_Operators | Microsoft Learn
DateTime.DayOfWeek Property | Microsoft Learn
DayOfWeek Enumeration | Microsoft Learn
:: Static Member Operator | Microsoft Learn

Other References

Other solutions to the problem | Stack Overflow

Created: Sunday, November 2, 2025

Updated: Sunday, November 2, 2025