import { rawdata } from './autogen.zonedata' ; import { RawData } from './types' ; import { binarySearch } from '@phensley/cldr-utils' ; export { RawData } from './types' ; public export interface ZoneInfo { /**
     * Time zone identifier.
     */ zoneid : string ; /**
     * Time zone 3-character abbreviation.
     */ abbr : string ; /**
     * Flag indicating the zone is currently in daylight savings time.
     */ dst : number ; /**
     * Time zone offset from UTC.
     */ offset : number ; } public export interface ZoneMeta { /**
     * Time zone identifier. Corrected if the metadata was looked up using an alias.
     */ zoneid : string ; /**
     * Current standard offset from UTC, in milliseconds.
     */ stdoffset : number ; /**
     * Latitude of the zone's "principal location".
     */ latitude : number ; /**
     * Longitude of the zone's "principal location".
     */ longitude : number ; /**
     * ISO 3166 2-character country code for all countries which
     * overlap the timezone.
     */ countries : string [ ] ; } public export interface Tz { /**
     * Get the info for a time zone using a UTC timestamp.
     */ fromUTC ( zoneid , utc ) : ZoneInfo | undefined ; /**
     * Get the info for a time zone using a Wall Clock timestamp. This returns
     * the corresponding UTC timestamp and the timezone info record, or undefined
     * if the zone lookup fails.
     *
     * Since daylight savings time can result in a clock being moved backwards,
     * there is some ambiguity in resolving a wall clock time to the corresponding
     * UTC time. For example, on November 8 2020 in New York, the time 1:30 AM
     * occurs twice: once before the timezone boundary of 2:00 AM is reached, and
     * again after the clock is set back 1 hour to 1:00 AM. This method attempts
     * to resolve the ambiguity in the most intuitive way.
     */ fromWall ( zoneid , wall ) : [ number , ZoneInfo ] | undefined ; /**
     * Resolve a lowercase time zone id or alias into the canonical proper-cased id.
     */ resolveId ( id ) : string | undefined ; /**
     * UTC zone info.
     */ utcZone ( ) : ZoneInfo ; /**
     * Metadata related to a zone, such as the list of country codes that overlap with
     * the zone, the latitude and longitude, and the current standard offset, in milliseconds.
     * These can be used to display user interfaces for selecting a zone.
     *
     * If the zone identifier does not match a known zone or alias this returns undefined.
     */ zoneMeta ( id ) : ZoneMeta | undefined ; /**
     * Returns an array of time zone ids.
     */ zoneIds ( ) : string [ ] ; } const numarray = ( s ) => ( s ? s . split ( ' ' ) . map ( ( n ) => parseInt ( n , 36 ) ) : [ ] ) ; public export class TzImpl { /** Mapping of canonical time zone ids to index */ private zoneindex : Map < string , number > = new Map ( ) ; /** Mapping of proper- and lower-cased time zone and alias ids to canonical time zone id */ private linkindex : Map < string , string > = new Map ( ) ; /** Array of time zone ids */ private _zoneids : string [ ] ; /** Array of untils and deltas */ private untilindex : number [ ] ; /** Raw un-decoded zone info */ private rawzoneinfo : string [ ] ; /** Decoded zone records */ private zonerecords : ZoneRecord [ ] ; /** Default UTC zone here for quick access */ private utcinfo : ZoneInfo = { zoneid : 'Etc/UTC' , abbr : 'UTC' , dst : 0 , offset : 0 } ; constructor ( raw ) { const zoneids = raw . zoneids . split ( '|' ) . map ( ( e , i ) => [ e , i ] as [ string , number ] ) ; const links = raw . links . split ( '|' ) . map ( ( e ) => { const [ k , j ] = e . split ( ':' ) ; return [ k , Number ( j ) ] as [ string , number ] ; } ) ; const addlink = ( src , dst ) => { const lowersrc = src . toLowerCase ( ) ; this . linkindex . set ( src , dst ) ; this . linkindex . set ( lowersrc , dst ) ; } ; this . _zoneids = [ ] ; zoneids . forEach ( ( i ) => { const id = i [ 0 ] ; this . _zoneids . push ( id ) ; this . zoneindex . set ( id , i [ 1 ] ) ; addlink ( id , id ) ; } ) ; links . forEach ( ( i ) => { const alias = i [ 0 ] ; const id = zoneids [ i [ 1 ] ] [ 0 ] ; addlink ( alias , id ) ; } ) ; this . untilindex = numarray ( raw . index ) ; this . rawzoneinfo = raw . zoneinfo ; this . zonerecords = new Array ( raw . zoneinfo . length ) ; raw . zoneids = '' ; raw . links = '' ; raw . index = '' ; } /**
     * Get the info for a time zone using a UTC timestamp.
     */ fromUTC ( zoneid , utc ) { const r = this . lookup ( zoneid , utc , true ) ; return r ? r [ 1 ] : r ; } /**
     * Get the info for a time zone using a local "wall clock" timestamp
     * for that zone.
     */ fromWall ( zoneid , wall ) { return this . lookup ( zoneid , wall , false ) ; } /**
     * UTC zone info.
     */ utcZone ( ) { return this . utcinfo ; } /**
     * Resolve a lowercase time zone id or alias into the canonical proper-cased id.
     */ resolveId ( id ) { return this . linkindex . get ( id ) ; } /**
     * Metadata related to a zone, such as the list of country codes that overlap with
     * the zone, the latitude and longitude, and the current standard offset, in milliseconds.
     * These can be used to display user interfaces for selecting a zone.
     */ zoneMeta ( id ) { const rec = this . record ( id ) ; if ( rec ) { return { zoneid : rec [ 0 ] , stdoffset : rec [ 1 ] . stdoff , latitude : rec [ 1 ] . latitude , longitude : rec [ 1 ] . longitude , countries : rec [ 1 ] . countries , } ; } return ; } /**
     * Returns an array of time zone ids.
     */ zoneIds ( ) { return this . _zoneids ; } /**
     * Lookup the time zone record and return the zone info.
     */ private lookup ( id , t , isutc ) { const rec = this . record ( id ) ; if ( rec ) { const [ zoneid , r ] = rec ; const [ utc , res ] = isutc ? r . fromUTC ( t ) : r . fromWall ( t ) ; return [ utc , { ... res , zoneid , } , ] ; } return undefined ; } /**
     * Find a record for a time zone id or alias.
     */ private record ( zoneid ) { const id = this . linkindex . get ( zoneid ) ; if ( id === undefined ) { return undefined ; } const i = this . zoneindex . get ( id ) ! ; let rec = this . zonerecords [ i ] ; if ( rec === undefined ) { const raw = this . rawzoneinfo [ i ] ; rec = new ZoneRecord ( raw , this . untilindex ) ; this . zonerecords [ i ] = rec ; this . rawzoneinfo [ i ] = '' ; } return [ id , rec ] ; } } internal class ZoneRecord { readonly stdoff : number ; readonly latitude : number ; readonly longitude : number ; readonly countries : string [ ] ; readonly localtime : ZoneInfoRec [ ] ; readonly types : number [ ] ; readonly untils : number [ ] ; readonly len : number ; constructor ( raw , index ) { const [ _std , _lat , _lon , _countries , _info , _types , _untils ] = raw . split ( '_' ) ; const untils = numarray ( _untils ) ; const types = _types ? _types . split ( '' ) . map ( ( t ) => TYPES [ t ] ) : [ ] ; const len = untils . length ; if ( len > 0 ) { untils [ 0 ] = index [ untils [ 0 ] ] * 1000 ; for ( let i = 1 ; i < len ; i ++ ) { untils [ i ] = untils [ i - 1 ] + index [ untils [ i ] ] * 1000 ; } } this . stdoff = parseInt ( _std , 36 ) * 1000 ; this . latitude = parseInt ( _lat , 36 ) / 1e6 ; this . longitude = parseInt ( _lon , 36 ) / 1e6 ; this . countries = _countries ? _countries . split ( ',' ) : [ ] ; this . localtime = _info . split ( '|' ) . map ( this . decodeInfo ) ; this . types = types ; this . untils = untils ; this . len = untils . length ; } /**
     * Resolve the zone info using a UTC timestamp.
     */ fromUTC ( utc ) { const i = binarySearch ( this . untils , true , utc ) ; const type = i === - 1 ? 0 : this . types [ i ] ; return [ utc , this . localtime [ type ] ] ; } /**
     * Resolve the zone info using a wall clock timestamp in the given zone.
     *
     * We have to determine the nearest DST transition boundary in wall clock
     * time, and choose one side of the boundary based on whether the clock moved
     * backwards or forwards, and where our wall time falls relative to
     * the boundary, or within the transitional gap.
     */ fromWall ( wall ) { let i = binarySearch ( this . untils , true , wall - 86400000 ) ; const r0 = this . localtime [ i === - 1 ? 0 : this . types [ i ] ] ; i ++ ; if ( i === this . types . length ) { return [ wall - r0 . offset , r0 ] ; } const r1 = this . localtime [ this . types [ i ] ] ; const u1 = this . untils [ i ] ; const w0 = u1 + r0 . offset ; const w1 = u1 + r1 . offset ; if ( wall < w0 && wall < w1 ) { return [ wall - r0 . offset , r0 ] ; } if ( w0 < w1 ) { return wall < w1 ? [ wall - r0 . offset , r1 ] : [ wall - r1 . offset , r1 ] ; } return wall < w0 ? [ wall - r0 . offset , r0 ] : [ wall - r1 . offset , r1 ] ; } /**
     * Decode a single zone info record.
     */ private decodeInfo ( raw ) { const [ abbr , _dst , _offset ] = raw . split ( ':' ) ; return { abbr , dst : Number ( _dst ) , offset : parseInt ( _offset , 36 ) * 1000 , } ; } } const TYPES = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' . split ( '' ) . reduce ( ( p , c , i ) => { p [ c ] = i ; return p ; } , { } as { [ x ] : number ; } ) ; interface ZoneInfoRec { abbr : string ; dst : number ; offset : number ; } public export const TZ = new TzImpl ( rawdata ) ;