There have been many attempts to get the correct Windows Install Date but 99% of the solutions I have seen are technically the wrong date. It was driving me bonkers in Windows 10.
Given the following, I was changing time zone in Powershell.
TL;DR
TL;DR
You must restart Powershell to produce the correct calculation, taking into account UTC time zone. If you change it while running current Powershell session it will be wrong.
Most Common Solutions for getting Windows Install Date are All Wrong
Here are most common way to find Windows Install Date;Note: I have changed the time zone from the system tray - expecting the correct result to appear. These will not adjust.
1. Most popular way, using following command line
filtering for Original Install Date outputs the wrong date.
2. The following Powershell is a common solution; checking WMI Win32_Registry class for InstallDate but outputs the wrong date.
3. The following Powershell is a common solution, checking the WMI Win32_OperatingSystem class for InstallDate outputs the wrong date.
4. The following Powershell is common solution checking the Windows Registry key
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\InstallDate
or using the C# equivalent snippet
will always output the wrong date, even in the C# snippet.
The definition of a UNIX timestamp is time zone independent. The UNIX timestamp is defined as the number of seconds that have elapsed since 00:00:00 Coordinated Universal Time (UTC) the Western European Time (WET) time zone, Thursday, 1 January 1970 and not counting leap seconds.
It does not store the original time zone, in which it was created. Or you could say it always stores the Western European Time (WET) timezone.
In other words, if you have installed you computer in Seattle, WA and moved to New York,NY the HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\InstallDate will give you the date in NY time zone, not in Seattle time zone where Windows was original installed. It's the wrong date, it doesn't store time zone info where the computer was initially installed.
1) Change you computer time zone (right-click on you clock->Adjust date/time->Adjust time zone) to the time zone where windows was installed, or first turned on.
Then run systeminfo.exe find /i "Original Install Date"
2) Get the UNIX Timestamp found in HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\InstallDate for me is 16-Feb-10 6:09:20 AM
This is the date with UTC+/-0:00, or "(UTC) Coordinated Universal Time" time zone. Add your target time zone to it.
3) Poweshell is complicated, since (Get-WmiObject Win32_Registry).InstallDate does store the original time zone as the format CIM_DATETIME (yyyymmddHHMMSS.mmmmmmsUUU) would suggest.
But when you interrogate (Get-WmiObject Win32_Registry).InstallDate in different time zones the value changes? by adding the current time zone to the value. Not what I would expect, this is a datatype and values should not change.
Runs show how ConvertToDateTime does not change over time zones?
Poweshell Script for above, download here.
1. Most popular way, using following command line
1 | systeminfo.exe | find /i "Original Install Date"
|
filtering for Original Install Date outputs the wrong date.
2. The following Powershell is a common solution; checking WMI Win32_Registry class for InstallDate but outputs the wrong date.
1 2 | (Get-WmiObject Win32_Registry).InstallDate
([WMI]'').ConvertToDateTime((Get-WmiObject Win32_Registry).InstallDate)
|
3. The following Powershell is a common solution, checking the WMI Win32_OperatingSystem class for InstallDate outputs the wrong date.
1 2 | (Get-WmiObject Win32_OperatingSystem).InstallDate
([WMI]'').ConvertToDateTime((Get-WmiObject Win32_OperatingSystem).InstallDate)
|
4. The following Powershell is common solution checking the Windows Registry key
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\InstallDate
1 2 | [TimeZone]::CurrentTimeZone.ToLocalTime([DateTime]'1.1.1970').AddSeconds( (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion').InstallDate ) |
or using the C# equivalent snippet
1 2 3 4 5 6 7 8 9 10 11 12 | var key = Microsoft.Win32.RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); key = key.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", false); if (key != null) { DateTime startDate = new DateTime(1970, 1, 1, 0, 0, 0); object objValue = key.GetValue("InstallDate"); string stringValue = objValue.ToString(); Int64 regVal = Convert.ToInt64(stringValue); DateTime installDate = startDate.AddSeconds(regVal); } |
will always output the wrong date, even in the C# snippet.
Culprit - Windows 10 resets install date now
In Windows 10, the install date will reset on each major named version release, like the last one - Version 1803 (April 2018 Update).
So what can you do?
You can get the date the System Volume Information was created for your Windows OS drive. This is sort of a proxy to the when the disk was originally created or in technically jargon partitioned (given, you never have reformatted your drive).
How to get Window OS partitioned create date
In Windows 10, search for “File Explorer Options” -> View tab and set “Show hidden files, folders, and drives”
On your system disk (C:\) where Windows is installed, and look for System Volume Information and hove over it and will show create date. Better still, you can choose "Date created" column to display as in image below.
There are caveats to this method, one is, if you reformat your drive you loose the first time it the initial partition was created.
Also, if you don't create the partition directly from Windows, this folder doesn't get created with it, automatically. The folder is created the first time Windows identifies the new partition. If you open Windows a few days later after the partition was created, the creation date listed for the System Volume Information folder will be different than the actual partition creation date.
Lastly, if you have only one partition, which you used to install/remove/install different operating systems, Windows will show the date when it created the folder, not when the partition started existing.
What is System Volume Information
System Volume Information is automatically created by Windows when it detects a new partition and it stores important things such as:
- System Restore points created by Windows for that partition (if it is set to create them).
- Distributed Link Tracking Service databases used to keep information about the creation and movement of linked files across your NTFS partitions.
- Indexing Service databases used for making fast file searches.
- Shadow Copies created by the Volume Snapshot Service that backups files specified by System Restore or Windows Backup.
Additional Notes - Powershell scripts may fail
1. ConvertToDateTime is not converting the CMI datetime datatype correctly period. See belowChanging timezone while running same Poweshell_ISE instance will fail
Now let me explain;The definition of a UNIX timestamp is time zone independent. The UNIX timestamp is defined as the number of seconds that have elapsed since 00:00:00 Coordinated Universal Time (UTC) the Western European Time (WET) time zone, Thursday, 1 January 1970 and not counting leap seconds.
It does not store the original time zone, in which it was created. Or you could say it always stores the Western European Time (WET) timezone.
In other words, if you have installed you computer in Seattle, WA and moved to New York,NY the HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\InstallDate will give you the date in NY time zone, not in Seattle time zone where Windows was original installed. It's the wrong date, it doesn't store time zone info where the computer was initially installed.
SOLUTION
You must restart Powershell to produce the correct calculation, taking into account UTC time zone. If you change it while running current Powershell session it will be wrong.
1) Change you computer time zone (right-click on you clock->Adjust date/time->Adjust time zone) to the time zone where windows was installed, or first turned on.
Then run systeminfo.exe find /i "Original Install Date"
2) Get the UNIX Timestamp found in HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\InstallDate for me is 16-Feb-10 6:09:20 AM
This is the date with UTC+/-0:00, or "(UTC) Coordinated Universal Time" time zone. Add your target time zone to it.
3) Poweshell is complicated, since (Get-WmiObject Win32_Registry).InstallDate does store the original time zone as the format CIM_DATETIME (yyyymmddHHMMSS.mmmmmmsUUU) would suggest.
But when you interrogate (Get-WmiObject Win32_Registry).InstallDate in different time zones the value changes? by adding the current time zone to the value. Not what I would expect, this is a datatype and values should not change.
Runs show how ConvertToDateTime does not change over time zones?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | Computername : THUNDERBALL-W7U Windows Install Current Time Zone : (UTC) Coordinated Universal Time Windows Install Date (ConvertToDateTime) : 16-Feb-10 1:09:20 AM Windows Install Date (MmgmtDTC::ToDateTime) : 16-Feb-10 1:09:20 AM Windows Install Date (ParseExact) : 16-Feb-10 6:09:20 AM Windows Install Date (ParseExact with TMZ offset 0) : 16-Feb-10 6:09:20 AM Windows Install Date (ParseExact BIAS removed, with TMZ offset 0) : 16-Feb-10 6:09:20 AM Windows Install Date (above & DST adjusted? - False) : 16-Feb-10 6:09:20 AM Windows Install Date Raw Value : 20100216060920.000000+000 Age : 7 years, 2 months, 3 days & 08 hours, 22 mins, 27 secs, 898 msecs Computername : THUNDERBALL-W7U Windows Install Current Time Zone : (UTC-05:00) Eastern Time (US & Canada) Windows Install Date (ConvertToDateTime) : 16-Feb-10 1:09:20 AM Windows Install Date (MmgmtDTC::ToDateTime) : 16-Feb-10 1:09:20 AM Windows Install Date (ParseExact) : 16-Feb-10 1:09:20 AM Windows Install Date (ParseExact with TMZ offset -300) : 15-Feb-10 8:09:20 PM Windows Install Date (ParseExact BIAS removed, with TMZ offset -300) : 16-Feb-10 1:09:20 AM Windows Install Date (above & DST adjusted? - False) : 16-Feb-10 1:09:20 AM Windows Install Date Raw Value : 20100216010920.000000-300 Age : 7 years, 2 months, 3 days & 13 hours, 21 mins, 55 secs, 180 msecs Computername : THUNDERBALL-W7U Windows Install Current Time Zone : (UTC-08:00) Pacific Time (US & Canada) Windows Install Date (ConvertToDateTime) : 16-Feb-10 1:09:20 AM Windows Install Date (MmgmtDTC::ToDateTime) : 16-Feb-10 1:09:20 AM Windows Install Date (ParseExact) : 15-Feb-10 10:09:20 PM Windows Install Date (ParseExact with TMZ offset -480) : 15-Feb-10 2:09:20 PM Windows Install Date (ParseExact BIAS removed, with TMZ offset -480) : 15-Feb-10 10:09:20 PM Windows Install Date (above & DST adjusted? - False) : 15-Feb-10 10:09:20 PM Windows Install Date Raw Value : 20100215220920.000000-480 Age : 7 years, 2 months, 3 days & 16 hours, 45 mins, 13 secs, 146 msecs |
Poweshell Script for above, download here.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | #requires -version 2.0 # ----------------------------------------------------------------------------- # Script: Get-WindowsInstallDateTMZAdjustedVersion3.ps1 # Version: 1.2017.07.08 # Author: Mark Pahulje # http://metadataconsulting.blogspot.com/ # Date: 08-Apr-2017 # Keywords: Registry, WMI, Windows Install Date # Comments: # # "Those who forget to script are doomed to repeat their work." # # **************************************************************** # * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED * # * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK. IF * # * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, * # * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING. * # **************************************************************** # ----------------------------------------------------------------------------- Function Get-WindowsInstallDateTMZAdjusted { <# .SYNOPSIS Get accurate Windows Install Date Time Zone (TMZ) Adjusted .DESCRIPTION This command uses WMI to retrieve install date of the Windows registry, which is equivallent to Windows Install Date that is time zone adjusted. Win32_Registry InstallDate is stored as WMI Datetime a datatype which really stores a Microsoft UTC format (yyyymmddHHMMSS.xxxxxx±UUU) where crucially ±UUU is number of minutes different local time zone (at the time Windows was installed) from Greenwich Mean Time and xxxxxx is milliseconds. This scripts add the time zone offset (±UUU) to get the real correct Windows install date. This version has no provision for alternate credentials. .MUNCHIES 99% of all scripts out there do not account for this! .OPTIONAL PARAMETER Computername The name of a computer to query. The default is the local host. .EXAMPLE PS C:\> Get-WindowsInstallDateTMZAdjusted Computername : THUNDERBALL-W7U Windows Install Current Time Zone : (UTC-05:00) Eastern Time (US & Canada) Windows Install Date (ConvertToDateTime) : 16-Feb-10 1:09:20 AM Windows Install Date (MmgmtDTC::ToDateTime) : 16-Feb-10 1:09:20 AM Windows Install Date (ParseExact) : 16-Feb-10 1:09:20 AM Windows Install Date (ParseExact with TMZ offset -300) : 15-Feb-10 8:09:20 PM Windows Install Date (ParseExact BIAS removed, with TMZ offset -300) : 16-Feb-10 1:09:20 AM Windows Install Date (above & DST adjusted? - False) : 16-Feb-10 1:09:20 AM Windows Install Date Raw Value : 20100216010920.000000-300 Age : 7 years, 2 months, 3 days & 13 hours, 21 mins, 55 secs, 180 msecs Return registry usage information for the local host. .EXAMPLE PS C:\> Get-Content Computers.txt | Get-WindowsInstallDateTMZAdjusted | Export-CSV c:\work\ListofComputerswithInstallDates.csv Retrieve registry install date (Windows install date) for all the computers in the text file, computers.txt. The results are exported to a CSV file. .NOTES NAME : Get-WindowsInstallDateTMZAdjusted VERSION : 1.2017.04.08 LAST UPDATED: 08-Apr-2017 AUTHOR : Martin Kohonen .LINK http://metadataconsulting.blogspot.ca/2017/04/How-to-get-the-most-accurate-Windows-Install-Date-time-zone-adjusted.html .LINK Get-WindowsInstallDateTMZAdjusted .INPUTS String .OUTPUTS A formatted table #> [cmdletbinding()] Param ( [Parameter(Position=0,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)] [ValidateNotNullorEmpty()] [String[]]$Computername=$env:Computername ) Begin { Write-Verbose "Starting $($myinvocation.mycommand)" } #Begin Process { Foreach ($computer in $computername) { Write-Verbose "Processing $computer" Try { #retrieve registry information via WMI $data=Get-WmiObject -Class Win32_Registry -ComputerName $computer -ErrorAction Stop $installdatestring = ($data).InstallDate $timeZone=Get-WmiObject -Class Win32_TimeZone -ComputerName $computer -ErrorAction Stop #UTC = local time - bias #https://msdn.microsoft.com/en-us/library/aa394498%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 #$timeZone=[TimeZoneInfo]::Local.DisplayName - this does not work when you change timezones # converts yyyymmddHHMMSS to datetime by default # $installdatetime = ([WMI]'').ConvertToDateTime($installdatestring); not trusting this function add +5:00 for some reason $yyyymmddHHMMSS = $installdatestring.Split('.')[0]; #[int]$year = [convert]::ToInt32($yyyymmddHHMMSS.Substring(0, 4),10); #[int]$month = [convert]::ToInt32($yyyymmddHHMMSS.Substring(4, 2),10); #[int]$day = [convert]::ToInt32($yyyymmddHHMMSS.Substring(4 + 2, 2),10); #[int]$hours = [convert]::ToInt32($yyyymmddHHMMSS.Substring(4 + 2 + 2, 2),10); #[int]$mins = [convert]::ToInt32($yyyymmddHHMMSS.Substring(4 + 2 + 2 + 2),10); #[int]$secounds = [convert]::ToInt32($yyyymmddHHMMSS.Substring(4 + 2 + 2 + 2 + 2, 4),10); #http://dusan.kuzmanovic.net/2012/05/07/powershell-parsing-date-and-time/ $template = 'yyyyMMddHHmmss' $installdatetime = [DateTime]::ParseExact($yyyymmddHHMMSS, $template, $null) $xxxxxxsUUU = $installdatestring.Split('.')[1]; [long]$microsecs = [convert]::ToInt64($xxxxxxsUUU.Substring(0, 6),10); [int]$UTCoffsetinMins = [convert]::ToInt32($xxxxxxsUUU.Substring(6, 4),10); [long]$millisecs = $microsecs*0.001 #https://msdn.microsoft.com/en-us/library/aa387237(v=vs.85).aspx $installdatetime = $installdatetime.AddMilliseconds($millisecs) #block output use assingment $installdatetimePE = $installdatetime #timezone added from InstallDate $installdatetimePETMZ = $installdatetimePE.AddMinutes($UTCoffsetinMins) #[TimeZoneInfo]::Local.BaseUtcOffset # does not work when you swith timezones! #[TimeZoneInfo]::Local.SupportsDaylightSavingTime #UTC = local time - bias #https://msdn.microsoft.com/en-us/library/aa394498%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396 $installdatetimeTMZ = $installdatetime.AddMinutes(($timeZone.Bias*-1)); #remove bias $isDST = $installdatetimeTMZ.IsDaylightSavingTime() if ($isDST){ $installdatetimeTMZ = $installdatetimeTMZ.AddMinutes(($timeZone.DaylightBias*-1)); #remove daylight saving bias, this was in the past not today, but doable } $installdatetimeTMZ = $installdatetimeTMZ.AddMinutes($UTCoffsetinMins); #add UTC Offset #direct call to .NET library [datetime]$InstallDateMngmtDTC = [System.Management.ManagementDateTimeConverter]::ToDateTime($installdatestring) #add a member to iterate over in our table - re http://windowsitpro.com/powershell/powershell-basics-custom-objects - great tip Add-Member -InputObject $data -MemberType NoteProperty ` -Name InstallDateRAW ` -Value $installdatetime Add-Member -InputObject $data -MemberType NoteProperty ` -Name InstallDateMngmtDTC ` -Value $InstallDateMngmtDTC Add-Member -InputObject $data -MemberType NoteProperty ` -Name InstallDatePE ` -Value $installdatetimePE Add-Member -InputObject $data -MemberType NoteProperty ` -Name InstallDatePETMZ ` -Value $installdatetimePETMZ Add-Member -InputObject $data -MemberType NoteProperty ` -Name InstallDateTMZ ` -Value $installdatetimeTMZ Add-Member -InputObject $data -MemberType NoteProperty ` -Name TimeZone ` -Value $timeZone.Caption #Format the results and write an object to the pipeline $data | Select-Object -Property @{Name="Computername";Expression={$_.__SERVER}}, @{Name="Windows Install Current Time Zone ";Expression={ $_.TimeZone }}, @{Name="Windows Install Date (ConvertToDateTime)";Expression={ $_.ConvertToDateTime($_.InstallDate) }}, @{Name="Windows Install Date (MmgmtDTC::ToDateTime)";Expression={ $_.InstallDateMngmtDTC }}, @{Name="Windows Install Date (ParseExact)";Expression={ $_.InstallDateRAW }}, @{Name="Windows Install Date (ParseExact with TMZ offset $UTCoffsetinMins)";Expression={ $_.InstallDatePETMZ }}, @{Name="Windows Install Date (ParseExact BIAS removed, with TMZ offset $UTCoffsetinMins)";Expression={ $_.InstallDatePE }}, @{Name="Windows Install Date (above & DST adjusted? - $isDST)";Expression={ $_.InstallDateTMZ }}, @{Name="Windows Install Date Raw Value";Expression={$_.InstallDate}}, @{Name="Age";Expression={"{1:N0} years, {2:N0} months, {3:N0} days & {0:hh} hours, {0:mm} mins, {0:ss} secs, {0:fff} msecs" -f ( ((Get-Date) - ($_.InstallDateTMZ)), [Math]::Truncate( (((Get-Date) - ($_.InstallDateTMZ)).Days/365.2425) ), [Math]::Truncate( ((((Get-Date) - ($_.InstallDateTMZ)).Days%365.2425)/30.436875)), ((((Get-Date) - ($_.InstallDateTMZ)).Days%30.436875)) ) }} } #try Catch { Write-Warning "Failed to retrieve registry information from $($Computer.ToUpper())" Write-Warning $_.Exception.Message }#Catch }#foreach $computer } #Process End { Write-Verbose "Ending $($myinvocation.mycommand)" } #End } #end function Get-WindowsInstallDateTMZAdjusted |
No comments:
Post a Comment