Some dates in Active Directory have Generalized-Time syntax. These dates are strings in the format "YYYYMMDDhhmmss.0Z". For example
"20141006133000.0Z" corresponds to October 6, 2014, at 13:30:00 (in UTC). This article dicusses how to deal with these dates.
Some of the Active Directory (AD) attributes with Generalized-Time syntax are documented in the following table. Sometimes this syntax
is called UTC Coded time, because the values are stored in AD
in UTC (Coordinated Universal Time, which used to be called GMT). "Replicated" in the table means the attribute is replicated to all domain
controllers in the
domain. "In GC" means the attribute is replicated to the
Global Catalog. "Operational" means the attribute is not actually saved in AD, but is constructed on request from other attributes.
The meetingStartTime and meetingEndTime attributes only apply to objects of class
Meeting. All of the other attributes in the above table apply to all objects in AD. The PowerShell properties Created and Modified are
supported by the Active Directory module cmdlets, like Get-ADUser, Get-ADComputer, and Get-ADObject. These properties convert the values
of the actual createTimeZone and modifyTimeZone attributes into datetime values in the local time zone. All of the attributes in the above table can only be updated by the system, except for meetingEndTime and meetingStartTime.
↑ Return to Top
The attributes createTimeStamp and whenCreated serve the same purpose. The same applies to the attributes modifyTimeStamp and whenChanged. The whenCreated and whenChanged attributes were added to the schema
first by Microsoft. Then the createTimeStamp and modifyTimeStamp attributes were added to conform with LDAP RFC 4512.
As shown in the table above, the Active Directory schema tells us that createTimeStamp and modifyTimeStamp are replicated to all domain controllers, but are not in the Global Catalog. The whenChanged attribute is not replicated, but somehow is in the Global
Catalog, while whenCreated is replicated and in the
Notice that createTimeStamp and modifyTimeStamp are both operational (sometimes called
constructed). This means the values are not saved in AD, but are constructed by the domain controller when you request the values. In this case, createTimeStamp is constructed from whenCreated and modifyTimeStamp is constructed from whenChanged. That explains
why the values of createTimeStamp and whenCreated are the same for any object, as long as you query one domain controller. The values of whenChanged and modifyTimeStamp also agree on any given domain controller.
The one exception to the statement that whenChanged and modifyTimeStamp for any object always agree on any domain controller is the "cn=Aggregate" object in the Schema container. This is discussed in a blog post by Christoffer Andersson, linked below in
the "Other Resources" section.
The values for the whenCreated attribute (and createTimeStamp) for any given object are the same on every domain controller. This is because whenCreated is replicated to all DC's. However, whenChanged is not replicated. The value of whenChanged (and modifyTimeStamp)
on each domain controller reflects when the last change was replicated to that DC. The values of whenChanged for any object can vary by a few seconds if the domain controllers are well connected. The differences will be larger if you have domain controllers
in remote sites with slow connections.
The whenChanged values for some objects can be much larger if the change happened before some of the domain controllers were promoted. In that case, the value of whenChanged will reflect when the DC was promoted, which is when the last change actually replicated
to that DC. The following table of example data illustrates this:
From the table above, we see that user jsmith was last modified 4/12/2014, and this lastModified date is reflected on all three domain controllers. User sjohnson was last modified 6/21/2014. But the lastModified dates are different on each DC for users ljones
and ajackson. This is because these objects were last modified before DC2 and DC3 were promoted to domain controllers. In fact, we can conclude that DC2 was promoted on 11/12/2013, since all users last modified before this date have this exact value for the
whenChanged attribute on DC2. That must be the date when these objects were first synchronized with AD on that domain controller. Similarly, we can conclude that DC3 was promoted on 2/15/2014. Notice that user fwilson was last modified 12/20/2013, which is
before DC3 was promoted, but after DC2 was promoted. The values of whenChanged for this user agree on domain controllers DC1 and DC2, which were in the domain when the change happened. But the value on DC3 for fwilson reflects when the user object was first
replicated to that domain controller.
If you were to check the values of the whenCreated attribute for these users, you would see that the values are the same on all three domain controllers. Even if the whenCreated dates are before the domain controller was promoted, the correct value synchronizes
from the other domain controllers during promotion.
Even though the schema indicates that createTimeStamp and modifyTimeStamp are not in the Global Catalog, you can still query the GC for these attributes because they are operational (also called constructed). Ordinarily, you cannot retrieve the value of
an attribute from the Global Catalog if the value is not replicated to the GC. For example, you cannot retrieve the value of the department attribute from a GC. However, if the attribute is operational, the GC will construct the value when you request it.
In this case, the values can be constructed from whenCreated or whenChanged, which are in the GC. However, since whenChanged is not replicated, you will only see values for this attribute in a GC for objects in the domain of the DC with the GC role.
The PowerShell Get-Date cmdlet supports a -UFormat parameter that can be used to retrieve the current date/time in Generalized-Time format. For example:
$Date = Get-Date -UFormat %Y%m%d%H%M%S.0Z
However, this cannot be used to convert any other datetime value into Generalized-Time format. For example, each of the following attempts raises an error:
$Date = (Get-Date -UFormat %Y%m%d%H%M%S.0Z).AddDays(-30)
$Date = (Get-Date).AddDays(-30) -UFormat %Y%m%d%H%M%S.0Z
Instead, a PowerShell function is required to convert any other date into Generalized-Time. For example:
# Convert PowerShell datetime to Generalized-Time.
# Ignore hours, minutes, and seconds.
Return $Date.Year.ToString() `
+ ("0" + $Date.Month.ToString()).Substring($($Date.Month.ToString()).Length - 1, 2) `
+ ("0" + $Date.Day.ToString()).Substring($($Date.Day.ToString()).Length - 1, 2) `
# Retrieve PowerShell date 30 days in the past.
$PSDate = (Get-Date).AddDays(-30)
"PowerShell Date: $PSDate"
# Convert to GeneralizeTime value.
$GTDate = PsDateToGenTime $PSDate
A VBScript function for the same purpose is similar:
' Function to convert datetime value into Generalized-Time.
' Ignore hours, minutes, and seconds.
DateToGenTime = CStr(Year(dtmValue)) _
& Right("0" & CStr(Month(dtmValue)), 2) _
& Right("0" & CStr(Day(dtmValue)), 2) _
' Retrieve a date 30 days in the past.
dtmDate = DateAdd("d", -30, Now())
Wscript.Echo "VBScript Date: " & CStr(dtmDate)
' Convert to Generalized-Time value.
Wscript.Echo "Generalized-Time: " & DateToGenTime(dtmDate)
These functions only deal with the date part, ignoring the time. If you modify the functions to deal with the time as well, then you must convert the datetime values from UTC to local time before converting to Generalized-Time.
The LDAP filter syntax is supported in PowerShell, VBScript, and many command line utilities like dsquery
adfind. In PowerShell, many AD module cmdlets support the -LDAPFilter parameter. The PowerShell code to retrieve all users created in the last 30 days using the -LDAPFilter parameter would be as follows:
$GTDate = "20141012133500.0Z"
Get-ADUser -LDAPFilter "(whenCreated>=$GTDate)" -Properties whenCreated | Select sAMAccountName, whenCreated
Notice the following about LDAP syntax filters:
The following table shows some example LDAP syntax filters with Generalized-Time attributes. The table shows filters with the whenChanged attribute, but the results are the same with any Generalized-Time attribute.
Some of the filters above do not raise an error but never produce results. Clearly, the filter should be enclosed in double quotes, and any variables or constants should not be quoted.
PowerShell documentation indicates that PowerShell syntax filters should be enclosed in braces. However, there are many examples where single quotes or double quotes are used instead. The following table shows some example PowerShell syntax filters with
Generalized-Time attributes. Only examples using braces are shown.