module TimezoneParser::Windows

Windows module

Constants

LCIDToLocaleName

function int LCIDToLocaleName ( In LCID Locale, Out_opt LPWSTR lpName, In int cchName, In DWORD dwFlags ); @see msdn.microsoft.com/en-us/library/windows/desktop/dd318698.aspx

LOCALE_NAME_MAX_LENGTH

Max locale length

TimeZonePath

Windows Registry path to Time Zone data

Public Class Methods

collectMUIOffsets(metazoneList, locales) click to toggle source
# File lib/timezone_parser/data/windows.rb, line 119
def self.collectMUIOffsets(metazoneList, locales)
    enUS = locales.key('en-US')
    baseMetazone = metazoneList[enUS]
    types = ['display', 'daylight', 'standard']
    type_bases = [0, 0, 0, 3, 3, 3, nil, 7, 7, 7]
    offsets = {}
    baseMetazone.each do |id, name|
        data = {}
        type = id % 10
        type_base = type_bases[type]
        data['Type'] = types[type - type_base]
        data['Name'] = baseMetazone[(id / 10) * 10 + type_base + types.index('standard')]
        offsets[id] = data unless data['Name'].nil?
    end
    Hash[offsets.to_a.sort_by { |o| o.first }]
end
correctMUIOffsetNames(offsets, metazoneList, locales) click to toggle source
# File lib/timezone_parser/data/windows.rb, line 136
def self.correctMUIOffsetNames(offsets, metazoneList, locales)
    enUS = locales.key('en-US')
    baseMetazone = metazoneList[enUS]
    offsets.each do |id, data|
        actualMetazone = nil
        baseMetazone.each do |zoneid, name|
            if id != zoneid and name == data['Name']
                actualMetazone = offsets[zoneid]['Name']
                break
            end
        end
        data['Name'] = actualMetazone if actualMetazone
    end
    offsets
end
errors() click to toggle source
# File lib/timezone_parser/data/windows.rb, line 18
def self.errors
    @@Errors
end
getLocales(lcids) click to toggle source
# File lib/timezone_parser/data/windows.rb, line 104
def self.getLocales(lcids)
    locales = {}
    lcids.each do |lcid|
        localeMem = Fiddle::Pointer.malloc(LOCALE_NAME_MAX_LENGTH)
        chars = LCIDToLocaleName.call(lcid, localeMem, LOCALE_NAME_MAX_LENGTH, 0)
        if chars.zero?
            puts "Warning: Failed to translate LCID (#{lcid}) to locale name!"
            next
        end
        locale = localeMem.to_s((chars-1)*2).force_encoding(Encoding::UTF_16LE).encode(Encoding::UTF_8)
        locales[lcid] = locale
    end
    locales
end
getMUIOffsets(path = TimeZonePath) click to toggle source
# File lib/timezone_parser/data/windows.rb, line 77
def self.getMUIOffsets(path = TimeZonePath)
    offsets = {}
    begin
        Win32::Registry::HKEY_LOCAL_MACHINE.open(path, Win32::Registry::KEY_READ).each_key do |key, wtime|
            Win32::Registry::HKEY_LOCAL_MACHINE.open(path + '\\' + key, Win32::Registry::KEY_READ) do |reg|
                muiDisplay = reg.read_s('MUI_Display')
                muiDlt = reg.read_s('MUI_Dlt')
                muiStd = reg.read_s('MUI_Std')

                offsets[self.parseMUI(muiDisplay)] = { 'Type' => 'display', 'Name' => key }
                offsets[self.parseMUI(muiDlt)] = { 'Type' => 'daylight', 'Name' => key }
                offsets[self.parseMUI(muiStd)] = { 'Type' => 'standard', 'Name' => key }
            end
        end
    rescue Win32::Registry::Error => e
        @@Errors << e.message
    end
    puts @@Errors unless @@Errors.empty?
    Hash[offsets.to_a.sort_by { |o| o.first }]
end
getTimezones(path = TimeZonePath) click to toggle source
# File lib/timezone_parser/data/windows.rb, line 34
def self.getTimezones(path = TimeZonePath)
    timezones = {}

    timezones['North Pacific Standard Time'] = { 'standard' => 3600 * -8, 'daylight' => 3600 * -7 }
    timezones['Russia TZ 5 Standard Time']   = { 'standard' => 3600 *  6, 'daylight' => 3600 *  6 }

    begin
        Win32::Registry::HKEY_LOCAL_MACHINE.open(path, Win32::Registry::KEY_READ).each_key do |key, wtime|
            Win32::Registry::HKEY_LOCAL_MACHINE.open(path + '\\' + key, Win32::Registry::KEY_READ) do |reg|
                timezones[key] ||= {}
                tzi = reg.read('TZI', Win32::Registry::REG_BINARY).last
                # TZI Structure (http://msdn.microsoft.com/en-us/library/windows/desktop/ms725481.aspx)
                # typedef struct _REG_TZI_FORMAT
                # {
                #    LONG Bias;
                #    LONG StandardBias;
                #    LONG DaylightBias;
                #    SYSTEMTIME StandardDate;
                #    SYSTEMTIME DaylightDate;
                # } REG_TZI_FORMAT;
                unpacked = tzi.unpack('lllSSSSSSSSSSSSSSSS')
                timezones[key]['standard'] = (0 - unpacked[0] - unpacked[1]) * 60
                timezones[key]['daylight'] = (0 - unpacked[0] - unpacked[2]) * 60
            end
        end
    rescue Win32::Registry::Error => e
        @@Errors << e.message
    end

    timezones = Hash[timezones.to_a.sort_by { |d| d.first } ]
    timezones
end
getTimezonesUTC() click to toggle source
# File lib/timezone_parser/data/windows.rb, line 67
def self.getTimezonesUTC()
    timezones = {}
    ((1..13).to_a + (-12..-1).to_a.reverse).each do |o|
        name = "UTC%+03d" % o
        timezones[name] = { 'standard' => 3600 * o }
        timezones[name]['daylight'] = timezones[name]['standard']
    end
    timezones
end
getVersion(path = TimeZonePath) click to toggle source
# File lib/timezone_parser/data/windows.rb, line 22
def self.getVersion(path = TimeZonePath)
    return @@Version if @@Version
    begin
        Win32::Registry::HKEY_LOCAL_MACHINE.open(path, Win32::Registry::KEY_READ) do |reg|
            @@Version = reg['TzVersion', Win32::Registry::REG_DWORD].to_s(16)
        end
    rescue Win32::Registry::Error => e
        @@Errors << e.message
    end
    @@Version
end
parseMUI(str) click to toggle source
# File lib/timezone_parser/data/windows.rb, line 98
def self.parseMUI(str)
    parts = str.split(',')
    puts "Warning: Unexpected dll name #{parts.first}" if parts.first != '@tzres.dll'
    parts.last.to_i.abs
end
parseMetazones(metazoneList, offsets, locales) click to toggle source
# File lib/timezone_parser/data/windows.rb, line 152
def self.parseMetazones(metazoneList, offsets, locales)
    metazones = {}
    metazoneList.each do |lcid, data|
        locale = locales[lcid]
        if locale.nil?
            puts "Warning: No translation to locale name from LCID (#{lcid}), skipping!"
            next
        end
        metazones[locale] = InsensitiveHash.new
        offsets.each do |id, info|
            unless data.has_key?(id)
                puts "Warning: Didn't found timezone name for #{id} for #{locale} locale, skipping!"
                next
            end
            name = data[id]
            metazones[locale][name] ||= {}
            metazones[locale][name]['Types'] ||= []
            metazones[locale][name]['Metazones'] ||= []
            types = []
            types << info['Type'] if info.has_key?('Type')
            if info['Type'] == 'display'
                types = ['daylight', 'standard']
            end
            metazones[locale][name]['Types'] += types
            metazones[locale][name]['Metazones'] << info['Name']
            metazones[locale][name]['Types'].uniq!
            metazones[locale][name]['Metazones'].uniq!
        end
        metazones[locale] = Hash[metazones[locale].to_a.sort_by { |d| d.first } ]
    end
    metazones = Hash[metazones.to_a.sort_by { |d| d.first } ]
    metazones
end