How to get the most accurate Windows Install Date (time zone adjusted)

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.


Most Common Solutions for getting Windows Install Date are All Wrong


Here are most common way to find Windows Install Date;

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[1] 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[2] 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. 



Reason why these will not work;


1  systeminfo.exe uses UNIX timestamp, but applies your current local time zone to the result when you run it.


2. ConvertToDateTime is not converting the CMI datetime datatype correctly period. See below

3. Ditto.

4. HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\InstallDate contains a UNIX timestamp is correct, it contains no times zone information.


Explanation


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 

[3]
Western European Time (WET) timezone. 

In other words, if you have installed you computer in Seattle, WA and moved to New York,NY the H KLM\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

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_DATE TIME[4] 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 is broken. 

   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  
  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                                : 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 TMZ adjusted                   : 16-Feb-10 6:09:20 AM  Windows Install Date Value                          : 20100216060920.000000+000  Age                                                 : 7 years, 1 months, 22.865625 days & 20 hours      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                                   : 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 TMZ adjusted                      : 15-Feb-10 10:09:20 PM  Windows Install Date Value                             : 20100215220920.000000-480  Age                                                    : 7 years, 1 months, 23.865625 days & 04 hours    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                                   : 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 TMZ adjusted                      : 16-Feb-10 1:09:20 AM  Windows Install Date Value                             : 20100216010920.000000-300  Age                                                    : 7 years, 1 months, 23.865625 days & 01 hours  

Poweshell Script for above.

    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  
  #requires -version 2.0     # -----------------------------------------------------------------------------  # Script: Get-WindowsInstallDateTMZAdjusted.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 Date (typical way) : 16-Feb-10 1:09:20 AM  Windows Install Date TMZ adjusted  : 15-Feb-10 8:09:20 PM  Windows Install Date Value         : 20100216010920.000000-300  Age                                : 7 years, 1 months, 21 days & 07 hours       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      :  Mark Pahulje  .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]$msecs = [convert]::ToInt64($xxxxxxsUUU.Substring(0, 6),10);           [int]$UTCoffsetinMins = [convert]::ToInt32($xxxxxxsUUU.Substring(6, 4),10);                      $installdatetime = $installdatetime.AddMilliseconds($msecs) #block output           $installdatetimePE = $installdatetime             #timezone added from InstallDate           $installdatetimePE = $installdatetimePE.AddMinutes($UTCoffsetinMins)             #SET YOUR TARGET TIMEZONE OF INITIAL            #(UTC-08:00) Pacific Time (US & Canada)              #[TimeZoneInfo]::Local.BaseUtcOffset # does not work when you swith timezones!            #[TimeZoneInfo]::Local.SupportsDaylightSavingTime                                           $installdatetimeTMZ =  $installdatetime.AddMinutes(($timeZone.Bias*-1));            $installdatetimeTMZ =  $installdatetimeTMZ.AddMinutes($UTCoffsetinMins);                         #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 InstallDatePE `          -Value $installdatetimePE                              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 ";Expression={ $_.InstallDateRAW }},                      @{Name="Windows Install Date (ParseExact with TMZ offset $UTCoffsetinMins)";Expression={ $_.InstallDatePE }},           @{Name="Windows Install Date TMZ adjusted";Expression={ $_.InstallDateTMZ }},           @{Name="Windows Install Date Value";Expression={$_.InstallDate}},                                                                             #THIS DOES ROUND UP DAYS need more work to calculate remainder and add it to hours           @{Name="Age";Expression={"{1:N0} years, {2:N0} months, {3} days & {0:hh} hours" -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     } 


Related Posts To How to get the most accurate Windows Install Date (time zone adjusted)


How to get the most accurate Windows Install Date (time zone adjusted) Rating: 4.5 Posted by: oliv7081

Search Here

Popular Posts

Total Pageviews

Recent Posts