Judul: Berbagi Info Seputar How to get the most accurate Windows Install Date (time zone adjusted) Full Update Terbaru
link: How to get the most accurate Windows Install Date (time zone adjusted)
Artikel Terbaru How to get the most accurate Windows Install Date (time zone adjusted) Update Terlengkap 2017
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.
Here are most common way to find Windows Install Date;
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[1] for InstallDate but outputs the wrong date.
3. The following Powershell is a common solution, checking the WMI Win32_OperatingSystem[2] 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.
1 systeminfo.exe uses UNIX timestamp will always be wrong.
2. ConvertToDateTime is not converting the datetime datatype correctly, timezone info is lost.
3. Ditto.
4. HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\InstallDate contains a UNIX timestamp and will always be wrong.
Now let me explain;
The definition of a UNIX timestamp[3] 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), Thursday, 1 January 1970 and not counting leap seconds.
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.
So what you really want is a data type that can accurately capture the time and date + time zone. This is where UTC Format[4] (UTC±HH:MM) comes in, it carries time zone information. The Microsoft(MS) engineers then optimized this for storage and came up with their own UTC format (yyyymmddHHMMSS.xxxxxx±UUU), striping hours, expanding minutes to be 4 digits with sign. This fits ±720 minutes (± 12 hrs or equivalent of 24hrs/time zones).
Fun Fact: Earth rotates at 15 degrees of longitude per hour. Each time zone[5] is 15 degrees wide or 1669.79km (1,040 miles) if perfectly divided. In 1 minute, earth rotates 28km (17.4 miles). At 89.8 degrees latitude it would be an easy walking speed of 6.4 km (4 mph) to match earths rotation, but you have to be 22.53km (14 miles) away from the North/South pole.
as per Microsoft TechNet article titled "Working with Dates and Times using WMI[6]".
So where is UTC Format stored for Windows Install Date?
Luckily, we can get timezone information from Installdate located in the WMI Win32_Registry class.[7]
Powershell Version
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 will always be wrong.
2. ConvertToDateTime is not converting the datetime datatype correctly, timezone info is lost.
3. Ditto.
4. HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\InstallDate contains a UNIX timestamp and will always be wrong.
Explanation
Now let me explain;
The definition of a UNIX timestamp[3] 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), Thursday, 1 January 1970 and not counting leap seconds.
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.
So what you really want is a data type that can accurately capture the time and date + time zone. This is where UTC Format[4] (UTC±HH:MM) comes in, it carries time zone information. The Microsoft(MS) engineers then optimized this for storage and came up with their own UTC format (yyyymmddHHMMSS.xxxxxx±UUU), striping hours, expanding minutes to be 4 digits with sign. This fits ±720 minutes (± 12 hrs or equivalent of 24hrs/time zones).
Fun Fact: Earth rotates at 15 degrees of longitude per hour. Each time zone[5] is 15 degrees wide or 1669.79km (1,040 miles) if perfectly divided. In 1 minute, earth rotates 28km (17.4 miles). At 89.8 degrees latitude it would be an easy walking speed of 6.4 km (4 mph) to match earths rotation, but you have to be 22.53km (14 miles) away from the North/South pole.
In the MS UTC format, dates are displayed as yyyymmddHHMMSS.xxxxxx+/-UUU, where:
- yyyy represents t he year.
- mm represents the month.
- dd represents the day.
- HH represents the hour (in 24-hour format).
- MM represents the minutes.
- SS represents the seconds.
- xxxxxx represents the milliseconds.
- ± represents positive or negative time zone (TMZ) offset
- UUU represents the time zone (TMZ) offset, the difference, in minutes, between the local time zone and Greenwich Mean Time (GMT).
as per Microsoft TechNet article titled "Working with Dates and Times using WMI[6]".
Example Microsoft UTC Format values look like
1 2 3 4 5 6 | 20100216010920.000000-300 20110106073639.000000+480 xxxxxxxxxxxxxx.000000+xxx --- general search patterns below for search engines ---- xxxxxxxxxxxxxx.000000-xxx ##############.000000+### ##############.000000-### |
So where is UTC Format stored for Windows Install Date?
Luckily, we can get timezone information from Installdate located in the WMI Win32_Registry class.
Sidenote: When you try to list all the software on you computer using the Win32_Product class[8] there a InstallDate which contains yyyymmdd and InstallDate2 which should contain a MS UTC Format, but warning this was not a required field for vendors and usually empty.
SOLUTION
Finally the solution, how to get an accurate timezone adjusted Windows Install Date in C#. Powershell is below this. 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 | /// <summary> /// Get Windows Registry InstallDate from Win32_Registry WMI key with UTC Offset (timezone added) /// </summary> /// <returns>A string formated to RFC1123 date format except add day of week and replace GMT with AM/PM</returns> /// <remarks> /// <author>Mark Pahulje, MetadataConsulting.ca - 2017</author> /// <description> /// /// Converting WMI Dates to a Standard Date-Time Format - <see href="https://technet.microsoft.com/en-us/library/ee198928.aspx"/> /// Working with Dates and Times using WMI - <see href="https://technet.microsoft.com/en-us/library/ee198928.aspx"/> /// /// InstallDate is in the UTC format (yyyymmddHHMMSS.xxxxxx±UUU) where critically /// /// xxxxxx is milliseconds and /// ±UUU is number of minutes different from Greenwich Mean Time. /// /// Examples /// 20100216010920.000000-300 /// 20110106073639.000000+480 /// /// TL;DR: The upshot is the accurate install date of Windows, adjusted to the time zone on the date of the install! /// </description> /// </remarks> private static string WindowsInstallDateTMZAdjusted() { DateTime InstallDate = new DateTime(1970, 1, 1, 0, 0, 0); //DUMMY LINE - NOT A UNIX timestamp as in most solutions ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_Registry"); foreach (ManagementObject wmi_Windows in searcher.Get()) { try { string installdate = wmi_Windows["InstallDate"].ToString(); //NOT a UNIX timestamp 99% of online solutions incorrect identify this as!!!! if (installdate.Length==25) { string yyyymmddHHMMSS = installdate.Split('.')[0]; string xxxxxxsUUU = installdate.Split('.')[1]; //±=s for sign int year = int.Parse(yyyymmddHHMMSS.Substring(0, 4)); int month = int.Parse(yyyymmddHHMMSS.Substring(4, 2)); int date = int.Parse(yyyymmddHHMMSS.Substring(4 + 2, 2)); int hour = int.Parse(yyyymmddHHMMSS.Substring(4 + 2 + 2, 2)); int mins = int.Parse(yyyymmddHHMMSS.Substring(4 + 2 + 2 + 2, 2)); int secs = int.Parse(yyyymmddHHMMSS.Substring(4 + 2 + 2 + 2 + 2, 2)); int msecs = int.Parse(xxxxxxsUUU.Substring(0, 6)); double UTCoffsetinMins = double.Parse(xxxxxxsUUU.Substring(6, 4)); TimeSpan UTCoffset = TimeSpan.FromMinutes(UTCoffsetinMins); InstallDate = new DateTime(year, month, date, hour, mins, secs, msecs) + UTCoffset; } break; } catch (Exception) { InstallDate = DateTime.Now; //fails now } } //choose your format return String.Format("{0:ddd d-MMM-yyyy h:mm:ss tt}", InstallDate); } |
Powershell Version
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 | #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 # converts yyyymmddHHMMSS to datetime by default $installdatetime = ([WMI]'').ConvertToDateTime($installdatestring); $xxxxxxsUUU = $installdatestring.Split('.')[1]; [long]$msecs = [convert]::ToInt64($xxxxxxsUUU.Substring(0, 6),10); [int]$UTCoffsetinMins = [convert]::ToInt32($xxxxxxsUUU.Substring(6, 4),10); $installdatetimeTMZ = $installdatetime.AddMinutes($UTCoffsetinMins).AddMilliseconds($msecs); #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 InstallDateTMZ ` -Value $installdatetimeTMZ #Format the results and write an object to the pipeline $data | Select-Object -Property @{Name="Computername";Expression={$_.__SERVER}}, @{Name="Windows Install Date (typical way)";Expression={ $_.ConvertToDateTime($_.InstallDate) }}, @{Name="Windows Install Date TMZ adjusted";Expression={ $_.InstallDateTMZ }}, @{Name="Windows Install Date Value";Expression={$_.InstallDate}}, @{Name="Age";Expression={"{1:N0} years, {2:N0} months, {3:N0} 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 } |