{*********************************************************}
{*                   OVCINTL.PAS 4.06                    *}
{*********************************************************}

{* ***** BEGIN LICENSE BLOCK *****                                            *}
{* Version: MPL 1.1                                                           *}
{*                                                                            *}
{* The contents of this file are subject to the Mozilla Public License        *}
{* Version 1.1 (the "License"); you may not use this file except in           *}
{* compliance with the License. You may obtain a copy of the License at       *}
{* http://www.mozilla.org/MPL/                                                *}
{*                                                                            *}
{* Software distributed under the License is distributed on an "AS IS" basis, *}
{* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License   *}
{* for the specific language governing rights and limitations under the       *}
{* License.                                                                   *}
{*                                                                            *}
{* The Original Code is TurboPower Orpheus                                    *}
{*                                                                            *}
{* The Initial Developer of the Original Code is TurboPower Software          *}
{*                                                                            *}
{* Portions created by TurboPower Software Inc. are Copyright (C)1995-2002    *}
{* TurboPower Software Inc. All Rights Reserved.                              *}
{*                                                                            *}
{* Contributor(s):                                                            *}
{*                                                                            *}
{* ***** END LICENSE BLOCK *****                                              *}

{$I OVC.INC}

{$B-} {Complete Boolean Evaluation}
{$I+} {Input/Output-Checking}
{$P+} {Open Parameters}
{$T-} {Typed @ Operator}
{.W-} {Windows Stack Frame}
{$X+} {Extended Syntax}

unit ovcintl;
  {-International date/time support class}

interface

uses
  {$IFNDEF LCL} Windows, Messages, {$ELSE} LclIntf, LMessages, LclType, MyMisc, {$ENDIF}
  Registry, Classes, Forms, SysUtils, OvcConst, OvcData, OvcStr, OvcDate;

type
  TCurrencySt = array[0..5] of AnsiChar;

  {.Z+}
  TIntlData = packed record
    {substitution strings for semi-literal mask characters}
    CurrencyLtStr : TCurrencySt; {corresponding string for 'c'}
    CurrencyRtStr : TCurrencySt; {corresponding string for 'C'}
    DecimalChar   : AnsiChar;    {character used for decimal point}
    CommaChar     : AnsiChar;    {character used for comma}
    {format specifiers for currency masks}
    CurrDigits    : Byte;        {number of dec places in currency}
    SlashChar     : AnsiChar;    {date seperator}
    {characters that represent boolean values}
    TrueChar      : AnsiChar;
    FalseChar     : AnsiChar;
    YesChar       : AnsiChar;
    NoChar        : AnsiChar;
  end;
  {.Z-}

type
  TOvcIntlSup = class(TObject)
  {.Z+}
  protected {private}
    FAutoUpdate      : Boolean;     {true to reset settings when win.ini changes}

    {substitution strings for semi-literal mask characters}
    FCurrencyLtStr   : TCurrencySt; {corresponding string for 'c'}
    FCurrencyRtStr   : TCurrencySt; {corresponding string for 'C'}
    FDecimalChar     : AnsiChar;    {character used for decimal point}

    {general international settings}
    FCommaChar       : AnsiChar;    {character used for comma}
    FCurrencyDigits  : Byte;        {number of dec places in currency}
    FListChar        : AnsiChar;    {list serarater}
    FSlashChar       : AnsiChar;    {character used to separate dates}

    {characters that represent boolean values}
    FTrueChar        : AnsiChar;
    FFalseChar       : AnsiChar;
    FYesChar         : AnsiChar;
    FNoChar          : AnsiChar;

    {event variables}
    FOnWinIniChange  : TNotifyEvent; {notify of win.ini changes}

    {internal working variables}
    intlHandle       : hWnd;  {our window handle}
    w1159            : array[0..5] of AnsiChar;
    w2359            : array[0..5] of AnsiChar;
    wColonChar       : AnsiChar;
    wCountry         : PAnsiChar;
    wCurrencyForm    : Byte;
    wldSub1          : array[0..5] of AnsiChar;
    wldSub2          : array[0..5] of AnsiChar;
    wldSub3          : array[0..5] of AnsiChar;
    wLongDate        : array[0..39] of AnsiChar;
    wNegCurrencyForm : Byte;
    wShortDate       : array[0..29] of AnsiChar;
    wTLZero          : Boolean;
    w12Hour          : Boolean;

    {property methods}
    function GetCountry : string;
    function GetCurrencyLtStr : string;
    function GetCurrencyRtStr : string;
    procedure SetAutoUpdate(Value : Boolean);
    procedure SetCurrencyLtStr(const Value : string);
    procedure SetCurrencyRtStr(const Value : string);

    {internal methods}
    procedure isExtractFromPicture(Picture, S : PAnsiChar; Ch : AnsiChar;
              var I : Integer; Blank, Default : Integer);
    procedure isIntlWndProc(var Msg : TMessage);
    function isMaskCharCount(P : PAnsiChar; MC : AnsiChar) : Word;
    procedure isMergeIntoPicture(Picture : PAnsiChar; Ch : AnsiChar; I : Integer);
    procedure isMergePictureSt(Picture, P : PAnsiChar; MC : AnsiChar; SP : PAnsiChar);
    procedure isPackResult(Picture, S : PAnsiChar);
    procedure isSubstChar(Picture : PAnsiChar; OldCh, NewCh : AnsiChar);
    procedure isSubstCharSim(P : PAnsiChar; OC, NC : AnsiChar);
    function isTimeToTimeStringPrim(Dest, Picture : PAnsiChar; T : TStTime;
             Pack : Boolean; t1159, t2359 : PAnsiChar) : PAnsiChar;

  public
    constructor Create;
    destructor Destroy;
      override;
  {.Z-}

    function CurrentDateString(const Picture : string;
      Pack : Boolean) : string;
  {.Z+}
    function CurrentDatePChar(Dest : PAnsiChar; Picture : PAnsiChar;
      Pack : Boolean) : PAnsiChar;
      {-returns today's date as a string of the specified form}
  {.Z-}

    function CurrentTimeString(const Picture : string; Pack : Boolean) : string;
  {.Z+}
    function CurrentTimePChar(Dest : PAnsiChar; Picture : PAnsiChar; Pack : Boolean) : PAnsiChar;
      {-returns current time as a string of the specified form}
  {.Z-}

    function DateToDateString(const Picture : string; Julian : TStDate;
      Pack : Boolean) : string;
  {.Z+}
    function DateToDatePChar(Dest : PAnsiChar; Picture : PAnsiChar; Julian : TStDate;
      Pack : Boolean) : PAnsiChar;
  {.Z-}
      {-convert Julian to a string of the form indicated by Picture}

    function DateStringToDMY(const Picture, S : string; var Day, Month, Year : Integer;
      Epoch : Integer) : Boolean;
  {.Z+}
    function DatePCharToDMY(Picture, S : PAnsiChar; var Day, Month, Year : Integer;
      Epoch : Integer) : Boolean;
  {.Z-}
      {-extract day, month, and year from S, returning true if string is valid}

    function DateStringIsBlank(const Picture, S : string) : Boolean;
  {.Z+}
    function DatePCharIsBlank(Picture, S : PAnsiChar) : Boolean;
  {.Z-}
      {-return True if the month, day, and year in S are all blank}

    function DateStringToDate(const Picture, S : string; Epoch : Integer) : TStDate;
  {.Z+}
    function DatePCharToDate(Picture, S : PAnsiChar; Epoch : Integer) : TStDate;
  {.Z-}
      {-convert St, a string of the form indicated by Picture, to a julian date. Picture and St must be of equal lengths}

    function DayOfWeekToString(WeekDay : TDayType) : string;
  {.Z+}
    function DayOfWeekToPChar(Dest : PAnsiChar; WeekDay : TDayType) : PAnsiChar;
  {.Z-}
      {-return a string for the specified day of the week}

    function DMYtoDateString(const Picture : string;
      Day, Month, Year : Integer; Pack : Boolean; Epoch : Integer) : string;
  {.Z+}
    function DMYtoDatePChar(Dest : PAnsiChar; Picture : PAnsiChar;
      Day, Month, Year : Integer; Pack : Boolean; Epoch : Integer) : PAnsiChar;
  {.Z-}
      {-merge the month, day, and year into the picture}

    function InternationalCurrency(FormChar : AnsiChar; MaxDigits : Byte; Float,
                                   AddCommas, IsNumeric : Boolean) : string;
  {.Z+}
    function InternationalCurrencyPChar(Dest : PAnsiChar; FormChar : AnsiChar;
                                        MaxDigits : Byte; Float,
                                        AddCommas, IsNumeric : Boolean) : PAnsiChar;
  {.Z-}
      {-return a picture mask for a currency string, based on Windows' intl info}

    function InternationalDate(ForceCentury : Boolean) : string;
  {.Z+}
    function InternationalDatePChar(Dest : PAnsiChar; ForceCentury : Boolean) : PAnsiChar;
  {.Z-}
      {-return a picture mask for a short date string, based on Windows' international information}

    function InternationalLongDate(ShortNames : Boolean; ExcludeDOW : Boolean) : string;
  {.Z+}
    function InternationalLongDatePChar(Dest : PAnsiChar; ShortNames : Boolean; ExcludeDOW : Boolean) : PAnsiChar;
  {.Z-}
      {-return a picture mask for a date string, based on Windows' international information}

    function InternationalTime(ShowSeconds : Boolean) : string;
  {.Z+}
    function InternationalTimePChar(Dest : PAnsiChar; ShowSeconds : Boolean) : PAnsiChar;
  {.Z-}
      {-return a picture mask for a time string, based on Windows' international information}

    function MonthStringToMonth(const S : string; Width : Byte) : Byte;
  {.Z+}
    function MonthPCharToMonth(S : PAnsiChar; Width : Byte) : Byte;
  {.Z-}
      {-Convert the month name in S to a month (1..12)}

    function MonthToString(Month : Integer) : string;
  {.Z+}
    function MonthToPChar(Dest : PAnsiChar; Month : Integer) : PAnsiChar;
  {.Z-}
      {return month name as a string for Month}

    procedure ResetInternationalInfo;
      {-read string resources and update internal info to match Windows'}

    function TimeStringToHMS(const Picture, S : string; var Hour, Minute, Second : Integer) : Boolean;
  {.Z+}
    function TimePCharToHMS(Picture, S : PAnsiChar; var Hour, Minute, Second : Integer) : Boolean;
  {.Z-}
      {-extract Hours, Minutes, Seconds from St, returning true if string is valid}

    function TimeStringToTime(const Picture, S : string) : TStTime;
  {.Z+}
    function TimePCharToTime(Picture, S : PAnsiChar) : TStTime;
  {.Z-}
      {-convert S, a string of the form indicated by Picture, to a Time variable}

    function TimeToTimeString(const Picture : string; T : TStTime; Pack : Boolean) : string;
  {.Z+}
    function TimeToTimePChar(Dest : PAnsiChar; Picture : PAnsiChar; T : TStTime; Pack : Boolean) : PAnsiChar;
  {.Z-}
      {-convert T to a string of the form indicated by Picture}

    function TimeToAmPmString(const Picture : string; T : TStTime; Pack : Boolean) : string;
  {.Z+}
    function TimeToAmPmPChar(Dest : PAnsiChar; Picture : PAnsiChar; T : TStTime; Pack : Boolean) : PAnsiChar;
  {.Z-}
      {-convert T to a string of the form indicated by Picture. Times are always displayed in am/pm format.}

    property AutoUpdate : Boolean
      read FAutoUpdate write SetAutoUpdate;
    property CurrencyLtStr : string
      read GetCurrencyLtStr write SetCurrencyLtStr;
    property CurrencyRtStr : string
      read GetCurrencyRtStr write SetCurrencyRtStr;
    property DecimalChar : AnsiChar
      read FDecimalChar write FDecimalChar;
    property CommaChar : AnsiChar
      read FCommaChar write FCommaChar;
    property Country : string
      read GetCountry;
    property CurrencyDigits : Byte
      read FCurrencyDigits write FCurrencyDigits;
    property ListChar : AnsiChar
      read FListChar write FListChar;
    property SlashChar : AnsiChar
      read FSlashChar write FSlashChar;
    property TrueChar : AnsiChar
      read FTrueChar write FTrueChar;
    property FalseChar : AnsiChar
      read FFalseChar write FFalseChar;
    property YesChar : AnsiChar
      read FYesChar write FYesChar;
    property NoChar  : AnsiChar
      read FNoChar write FNoChar;
    property OnWinIniChange : TNotifyEvent
      read FOnWinIniChange write FOnWinIniChange;
  end;

const
  DefaultIntlData : TIntlData = (
    {substitution strings for semi-literal mask characters}
    CurrencyLtStr : '$'; {corresponding string for 'c'}
    CurrencyRtStr : '';  {corresponding string for 'C'}
    DecimalChar   : '.'; {character used for decimal point}
    CommaChar     : ','; {character used for comma}
    {format specifiers for currency masks}
    CurrDigits    : 2;   {number of dec places in currency}
    SlashChar     : '/'; {date seperator}
    {characters that represent boolean values}
    TrueChar      : 'T';
    FalseChar     : 'F';
    YesChar       : 'Y';
    NoChar        : 'N');

var
  {global default international support object}
  OvcIntlSup : TOvcIntlSup;

implementation

{*** Inline routines ***}

{$IFDEF NoAsm}
function GetMaxWord(A, B : Word) : Word;
begin
  if A >= B then
    Result := A
  else
    Result := B;
end;
{$ELSE}
function GetMaxWord(A, B : Word) : Word; register;
  {-Return the greater of A and B}
asm
  and    eax,0FFFFH {faster than movzx              }
  and    edx,0FFFFH {faster than movzx              }
  cmp    eax,edx    {compare A and B                }
  jae    @@001      {done if ax is greater or equal }
  mov    eax,edx    {dx is larger, set result       }
@@001:
end;
{$ENDIF}

{*** TOvcIntlSup ***}

constructor TOvcIntlSup.Create;
begin
  inherited Create;

  FAutoUpdate      := False;

  {substitution strings for semi-literal mask characters}
  StrCopy(FCurrencyLtStr, DefaultIntlData.CurrencyLtStr);
  StrCopy(FCurrencyRtStr, DefaultIntlData.CurrencyRtStr);
  FDecimalChar     := DefaultIntlData.DecimalChar;
  FCommaChar       := DefaultIntlData.CommaChar;

  {format specifiers for currency masks}
  FCurrencyDigits  := DefaultIntlData.CurrDigits;
  FSlashChar       := DefaultIntlData.SlashChar;

  {characters that represent boolean values}
  FTrueChar        := DefaultIntlData.TrueChar;
  FFalseChar       := DefaultIntlData.FalseChar;
  FYesChar         := DefaultIntlData.YesChar;
  FNoChar          := DefaultIntlData.NoChar;

  {get windows international information}
  ResetInternationalInfo;
end;

function TOvcIntlSup.CurrentDateString(const Picture : string;
  Pack : Boolean) : string;
  {-returns today's date as a string of the specified form}
begin
  Result := DateToDateString(Picture, CurrentDate, Pack);
end;

function TOvcIntlSup.CurrentDatePChar(Dest : PAnsiChar; Picture : PAnsiChar;
  Pack : Boolean) : PAnsiChar;
  {-returns today's date as a string of the specified form}
begin
  Result := DateToDatePChar(Dest, Picture, CurrentDate, Pack);
end;

function TOvcIntlSup.CurrentTimeString(const Picture : string; Pack : Boolean) : string;
  {-returns current time as a string of the specified form}
begin
  Result := TimeToTimeString(Picture, CurrentTime, Pack);
end;

function TOvcIntlSup.CurrentTimePChar(Dest : PAnsiChar; Picture : PAnsiChar; Pack : Boolean) : PAnsiChar;
  {-returns current time as a string of the specified form}
begin
  Result := TimeToTimePChar(Dest, Picture, CurrentTime, Pack);
end;

function TOvcIntlSup.DateStringIsBlank(const Picture, S : string) : Boolean;
  {-return True if the month, day, and year in S are all blank}
var
  Buf1 : array[0..255] of AnsiChar;
  Buf2 : array[0..255] of AnsiChar;
begin
  StrPCopy(Buf1, Picture);
  StrPCopy(Buf2, S);
  Result := DatePCharIsBlank(Buf1, Buf2);
end;

function TOvcIntlSup.DatePCharIsBlank(Picture, S : PAnsiChar) : Boolean;
  {-return True if the month, day, and year in S are all blank}
var
  M, D, Y : Integer;
begin
  isExtractFromPicture(Picture, S, pmMonthName,  M, -2, 0);
  if M = 0 then
    isExtractFromPicture(Picture, S, pmMonth, M, -2, -2);
  isExtractFromPicture(Picture, S, pmDay,   D, -2, -2);
  isExtractFromPicture(Picture, S, pmYear,  Y, -2, -2);
  Result := (M = -2) and (D = -2) and (Y = -2);
end;

function TOvcIntlSup.DateStringToDate(const Picture, S : string; Epoch : Integer) : TStDate;
  {-convert St, a string of the form indicated by Picture, to a julian date.
    Picture and St must be of equal lengths}
var
  Buf1 : array[0..255] of AnsiChar;
  Buf2 : array[0..255] of AnsiChar;
begin
  StrPCopy(Buf1, Picture);
  StrPCopy(Buf2, S);
  Result := DatePCharToDate(Buf1, Buf2, Epoch);
end;

function TOvcIntlSup.DatePCharToDate(Picture, S : PAnsiChar; Epoch : Integer) : TStDate;
  {-convert St, a string of the form indicated by Picture, to a julian date.
    Picture and St must be of equal lengths}
var
  Month, Day, Year : Integer;
begin
  {extract day, month, year from St}
  if DatePCharToDMY(Picture, S, Day, Month, Year, Epoch) then
    {convert to julian date}
    Result := DMYtoStDate(Day, Month, Year, Epoch)
  else
    Result := BadDate;
end;

function TOvcIntlSup.DateStringToDMY(const Picture, S : string; var Day, Month, Year : Integer;
  Epoch : Integer) : Boolean;
  {-extract day, month, and year from S, returning true if string is valid}
var
  Buf1 : array[0..255] of AnsiChar;
  Buf2 : array[0..255] of AnsiChar;
begin
  StrPCopy(Buf1, Picture);
  StrPCopy(Buf2, S);
  Result := DatePCharToDMY(Buf1, Buf2, Day, Month, Year, Epoch);
end;

function TOvcIntlSup.DatePCharToDMY(Picture, S : PAnsiChar; var Day, Month, Year : Integer;
  Epoch : Integer) : Boolean;
  {-extract day, month, and year from S, returning true if string is valid}
begin
  Result := False;
  if StrLen(Picture) <> StrLen(S) then
    Exit;

  isExtractFromPicture(Picture, S, pmMonthName, Month, -1, 0);
  if Month = 0 then
    isExtractFromPicture(Picture, S, pmMonth, Month, -1, DefaultMonth);
  isExtractFromPicture(Picture, S, pmDay, Day, -1, 1);
  isExtractFromPicture(Picture, S, pmYear, Year, -1, DefaultYear);
  Result := ValidDate(Day, Month, Year, Epoch);
end;

function TOvcIntlSup.DateToDateString(const Picture : string;
         Julian : TStDate; Pack : Boolean) : string;
  {-convert Julian to a string of the form indicated by Picture}
var
  Buf1 : array[0..255] of AnsiChar;
  Buf2 : array[0..255] of AnsiChar;
begin
  StrPCopy(Buf1, Picture);
  Result := StrPas(DateToDatePChar(Buf2, Buf1, Julian, Pack));
end;

function TOvcIntlSup.DateToDatePChar(Dest : PAnsiChar; Picture : PAnsiChar;
         Julian : TStDate; Pack : Boolean) : PAnsiChar;
  {-convert Julian to a string of the form indicated by Picture}
var
  Month, Day, Year : Integer;
begin
  Move(Picture[0], Dest[0], StrLen(Picture)+1);
  if Julian = BadDate then begin
    {map picture characters to spaces}
    isSubstChar(Dest, pmMonth,   ' ');
    isSubstChar(Dest, pmMonthName,    ' ');
    isSubstChar(Dest, pmDay,     ' ');
    isSubstChar(Dest, pmYear,    ' ');
    isSubstChar(Dest, pmWeekDay, ' ');
    isMergePictureSt(Picture, Dest, pmLongDateSub1, wldSub1);
    isMergePictureSt(Picture, Dest, pmLongDateSub2, wldSub2);
    isMergePictureSt(Picture, Dest, pmLongDateSub3, wldSub3);

    {map slashes}
    isSubstChar(Dest, pmDateSlash, SlashChar);

    Result := Dest;
  end else begin
    {convert Julian to day/month/year}
    StDateToDMY(Julian, Day, Month, Year);
    {merge the month, day, and year into the picture}
    Result := DMYtoDatePChar(Dest, Picture, Day, Month, Year, Pack, 0);
  end;
end;

function TOvcIntlSup.DayOfWeekToString(WeekDay : TDayType) : string;
  {-return the day of the week specified by WeekDay as a string. Will
    honor the international names as specified in the INI file.}
begin
  Result := LongDayNames[Ord(WeekDay)+1];
end;

function TOvcIntlSup.DayOfWeekToPChar(Dest : PAnsiChar; WeekDay : TDayType) : PAnsiChar;
  {-return the day of the week specified by WeekDay as a string in Dest. Will
    honor the international names as specified in the INI file.}
begin
  Result := Dest;
  StrPCopy(Dest, LongDayNames[Ord(WeekDay)+1]);
end;

destructor TOvcIntlSup.Destroy;
begin
{$IFNDEF LCL}
  if intlHandle <> 0 then
  {$IFDEF VERSION6}
    Classes.DeallocateHWnd(intlHandle);
  {$ELSE}
    DeallocateHWnd(intlHandle);
  {$ENDIF}
{$ENDIF}
  StrDispose(wCountry);
  inherited Destroy;
end;

function TOvcIntlSup.DMYtoDateString(const Picture : string; Day, Month,
         Year : Integer; Pack : Boolean; Epoch : Integer) : string;
  {-merge the month, day, and year into the picture}
var
  Buf1 : array[0..255] of AnsiChar;
  Buf2 : array[0..255] of AnsiChar;
begin
  StrPCopy(Buf1, Picture);
  Result := StrPas(DMYtoDatePChar(Buf2, Buf1, Day, Month, Year, Pack, Epoch));
end;

function TOvcIntlSup.DMYtoDatePChar(Dest : PAnsiChar; Picture : PAnsiChar; Day, Month,
         Year : Integer; Pack : Boolean; Epoch : Integer) : PAnsiChar;
  {-merge the month, day, and year into the picture}
var
  DOW       : Integer;
  EpochCent : Integer;
begin
  Move(Picture[0], Dest[0], StrLen(Picture)+1);

  EpochCent := (Epoch div 100)*100;
  if Word(Year) < 100 then begin
    if Year < (Epoch mod 100) then
      Inc(Year, EpochCent + 100)
    else
      Inc(Year, EpochCent)
  end;

  DOW := Integer(DayOfWeekDMY(Day, Month, Year, Epoch));
  isMergeIntoPicture(Dest, pmMonth, Month);
  isMergeIntoPicture(Dest, pmDay, Day);
  isMergeIntoPicture(Dest, pmYear, Year);
  isMergeIntoPicture(Dest, pmMonthName, Month);
  isMergeIntoPicture(Dest, pmWeekDay, DOW);

  {map slashes}
  isSubstChar(Dest, pmDateSlash, SlashChar);

  isMergePictureSt(Picture, Dest, pmLongDateSub1, wldSub1);
  isMergePictureSt(Picture, Dest, pmLongDateSub2, wldSub2);
  isMergePictureSt(Picture, Dest, pmLongDateSub3, wldSub3);

  if Pack then
    isPackResult(Picture, Dest);

  Result := Dest;
end;

function TOvcIntlSup.GetCountry : string;
  {-return the country setting}
begin
  Result := StrPas(wCountry);
end;

function TOvcIntlSup.GetCurrencyLtStr : string;
begin
  Result := StrPas(FCurrencyLtStr);
end;

function TOvcIntlSup.GetCurrencyRtStr : string;
begin
  Result := StrPas(FCurrencyRtStr);
end;

function TOvcIntlSup.InternationalCurrency(FormChar : AnsiChar; MaxDigits : Byte; Float,
         AddCommas, IsNumeric : Boolean) : string;
  {-Return a picture mask for a currency string, based on Windows' intl info}
var
  Buf1 : array[0..255] of AnsiChar;
begin
  Result := StrPas(InternationalCurrencyPChar(Buf1, FormChar, MaxDigits,
            Float, AddCommas, IsNumeric));
end;

function TOvcIntlSup.InternationalCurrencyPChar(Dest : PAnsiChar; FormChar : AnsiChar;
         MaxDigits : Byte; Float, AddCommas, IsNumeric : Boolean) : PAnsiChar;
  {-Return a picture mask for a currency string, based on Windows' intl info}
const
  NP : array[0..1] of AnsiChar = pmNegParens+#0;
  NH : array[0..1] of AnsiChar = pmNegHere+#0;
var
  CLSlen, DLen, I, J : Word;
  Tmp : array[0..10] of AnsiChar;
begin
  Dest[0] := #0;
  Result := Dest;

  if (MaxDigits = 0) then
    Exit;

  {initialize Dest with the numeric part of the string to left of decimal point}
  I := Pred(MaxDigits) div 3 ;
  J := Word(MaxDigits)+(I*Ord(AddCommas));
  if J > 247 then
    DLen := 247
  else
    DLen := J;
  FillChar(Dest[0], DLen, FormChar);
  Dest[DLen] := #0;

  if AddCommas then begin
    {insert commas at appropriate points}
    J := 0;
    for I := DLen-1 downto 0 do
      if J < 3 then
        Inc(J)
      else begin
        Dest[I] := pmComma;
        J := 0;
      end;
  end;

  {add in the decimals}
  if CurrencyDigits > 0 then begin
    Dest[DLen] := pmDecimalPt;
    FillChar(Dest[DLen+1], CurrencyDigits, FormChar);
    Inc(DLen, CurrencyDigits+1);
    Dest[DLen] := #0;
  end;

  {do we need a minus before the currency symbol}
  if (wNegCurrencyForm = 6) then
    StrCat(Dest, NH);

  {see if we can do a floating currency symbol}
  if Float then
    Float := not Odd(wCurrencyForm);

  {plug in the picture characters for the currency symbol}
  CLSlen := StrLen(FCurrencyLtStr);
  if Float then
    StrStInsertPrim(Dest, CharStrPChar(Tmp, pmFloatDollar, CLSlen), 0)
  else if not Odd(wCurrencyForm) then
    StrStInsertPrim(Dest, CharStrPChar(Tmp, pmCurrencyLt, CLSlen), 0)
  else
    StrCat(Dest, CharStrPChar(Tmp, pmCurrencyRt, StrLen(FCurrencyRtStr)));

  {plug in special minus characters}
  if IsNumeric then
    case wNegCurrencyForm of
      0, 4 :
        StrCat(Dest, NP);
      3, 7, 10 :
        if Odd(wCurrencyForm) then
          StrCat(Dest, NH);
    end;
end;

function TOvcIntlSup.InternationalDate(ForceCentury : Boolean) : string;
  {-return a picture mask for a short date string, based on Windows' international information}
var
  Buf : array[0..255] of AnsiChar;
begin
  InternationalDatePChar(Buf, ForceCentury);
  Result := StrPas(Buf);
end;

function TOvcIntlSup.InternationalDatePChar(Dest : PAnsiChar;
                                            ForceCentury : Boolean) : PAnsiChar;
  {-return a picture mask for a date string, based on Windows' int'l info}


  procedure FixMask(MC : AnsiChar; DL : Integer);
  var
    I     : Cardinal;
    J, AL : Word;
    MCT   : AnsiChar;
    Found : Boolean;
  begin
    {find number of matching characters}
    MCT := MC;

    Found := StrChPos(Dest, MC, I);
    if not Found then begin
      MCT := UpCase(MC);
      Found := StrChPos(Dest, MCT, I);
    end;
    if not Found then
      Exit;

    {pad substring to desired length}
    AL := isMaskCharCount(Dest, MCT);
    if AL < DL then
      for J := 1 to DL-AL do
        StrChInsertPrim(Dest, MCT, I);


    if MC <> pmYear then
      {choose blank/zero padding}
      case AL of
        1 : if MCT = MC then
              isSubstCharSim(Dest, MCT, UpCase(MCT));
        2 : if MCT <> MC then
              isSubstCharSim(Dest, MCT, MC);
      end;
  end;

begin
  {copy Windows mask into our var}
  StrCopy(Dest, wShortDate);

  {if single Day marker, make double}
  FixMask(pmDay, 2);

  {if single Month marker, make double}
  FixMask(pmMonth, 2);

  {force yyyy if desired}
  FixMask(pmYear, 2 shl Ord(ForceCentury));

  Result := Dest;
end;

function TOvcIntlSup.InternationalLongDate(ShortNames : Boolean; ExcludeDOW : Boolean) : string;
  {-return a picture mask for a date string, based on Windows' int'l info}
var
  Buf : array[0..255] of AnsiChar;
begin
  Result := StrPas(InternationalLongDatePChar(Buf, ShortNames, ExcludeDOW));
end;

function TOvcIntlSup.InternationalLongDatePChar(Dest : PAnsiChar; ShortNames : Boolean;
         ExcludeDOW : Boolean) : PAnsiChar;
  {-return a picture mask for a date string, based on Windows' int'l info}
var
  I    : Cardinal;
  WC   : Word;
  Temp : array[0..80] of AnsiChar;
  Stop : Boolean;

  function LongestMonthName : Word;
  var
    I : Word;
  begin
    Result := 0;
    for I := 1 to 12 do
      Result := GetMaxWord(Result, Length(LongMonthNames[I]));
  end;

  function LongestDayName : Word;
  var
    D : TDayType;
  begin
    Result := 0;
    for D := Sunday to Saturday do
      Result := GetMaxWord(Result, Length(LongDayNames[Ord(D)+1]));
  end;

  procedure FixMask(MC : AnsiChar; DL : Integer);
  var
    I     : Cardinal;
    J, AL : Word;
    MCT   : AnsiChar;
    Found : Boolean;
  begin
    {find first matching mask character}
    MCT := MC;
    Found := StrChPos(Temp, MC, I);
    if not Found then begin
      MCT := UpCase(MC);
      Found := StrChPos(Temp, MCT, I);
    end;
    if not Found then
      Exit;

    {pad substring to desired length}
    AL := isMaskCharCount(Temp, MCT);
    if AL < DL then begin
      for J := 1 to DL-AL do
        StrChInsertPrim(Temp, MCT, I);
    end else if (AL > DL) then
      StrStDeletePrim(Temp, I, AL-DL);

    if MC <> pmYear then
      {choose blank/zero padding}
      case AL of
        1 : if MCT = MC then
              isSubstCharSim(Temp, MCT, UpCase(MCT));
        2 : if MCT <> MC then
              isSubstCharSim(Temp, MCT, MC);
      end;
  end;

begin
  {copy Windows mask into temporary var}
  StrCopy(Temp, wLongDate);

  if ExcludeDOW then begin
    {remove day-of-week and any junk that follows}
    if StrChPos(Temp, pmWeekDay, I) then begin
      WC := 1;
      Stop := False;
      repeat
        case LoCaseChar(Temp[I+WC]) of
          #0, pmMonth, pmDay, pmYear, pmMonthName : Stop := True;
        else
          Inc(WC);
        end;
      until Stop;
      StrStDeletePrim(Temp, I, WC);
    end;
  end else if ShortNames then
    FixMask(pmWeekDay, 3)
  else if isMaskCharCount(Temp, pmWeekday) = 4 then
    FixMask(pmWeekDay, LongestDayName);

  {fix month names}
  if ShortNames then
    FixMask(pmMonthName, 3)
  else if isMaskCharCount(Temp, pmMonthName) = 4 then
    FixMask(pmMonthName, LongestMonthName);

  {if single Day marker, make double}
  FixMask(pmDay, 2);

  {if single Month marker, make double}
  FixMask(pmMonth, 2);

  {force yyyy}
  FixMask(pmYear, 4);

  {copy result into Dest}
  StrCopy(Dest, Temp);
  Result := Dest;
end;

function TOvcIntlSup.InternationalTime(ShowSeconds : Boolean) : string;
  {-return a picture mask for a time string, based on Windows' int'l info}
var
  Buf : array[0..255] of AnsiChar;
begin
  Result := StrPas(InternationalTimePChar(Buf, ShowSeconds));
end;

function TOvcIntlSup.InternationalTimePChar(Dest : PAnsiChar; ShowSeconds : Boolean) : PAnsiChar;
  {-return a picture mask for a time string, based on Windows' int'l info}
var
  SL, ML : Word;
  S : array[0..20] of AnsiChar;
begin
  {format the default string}
  StrCopy(S, 'hh:mm:ss');
  if not wTLZero then
    S[0] := pmHourU;

  {show seconds?}
  if not ShowSeconds then
    S[5] := #0;

  {handle international AM/PM markers}
  if w12Hour then begin
    ML := GetMaxWord(StrLen(@w1159), StrLen(@w2359));
    if (ML <> 0) then begin
      SL := StrLen(S);
      S[SL] := ' ';
      FillChar(S[SL+1], ML, pmAmPm);
      S[SL+ML+1] := #0;
    end;
  end;

  StrCopy(Dest, S);
  Result := Dest;
end;

procedure TOvcIntlSup.isIntlWndProc(var Msg : TMessage);
  {-window procedure to catch WM_WININICHANGE messages}
begin
  with Msg do
    if AutoUpdate and (Msg = WM_WININICHANGE) then
      try
        if Assigned(FOnWinIniChange) then
          FOnWinIniChange(Self)
        else
          ResetInternationalInfo;
      except
        Application.HandleException(Self);
      end
    else
      Result := DefWindowProc(intlHandle, Msg, wParam, lParam);
end;

procedure TOvcIntlSup.isExtractFromPicture(Picture, S : PAnsiChar;
                             Ch : AnsiChar; var I : Integer;
                             Blank, Default : Integer);
  {-extract the value of the subfield specified by Ch from S and return in
    I. I will be set to -1 in case of an error, Blank if the subfield exists
    in Picture but is empty, Default if the subfield doesn't exist in
    Picture.}
var
  PTmp    : Array[0..20] of AnsiChar;
  J, K, W : Cardinal;
  Code    : Integer;
  Found,
  UpFound : Boolean;
begin
  {find the start of the subfield}
  I := Default;
  Found := StrChPos(Picture, Ch, J);
  Ch := UpCaseChar(Ch);
  UpFound := StrChPos(Picture, Ch, K);

  if not Found or (UpFound and (K < J)) then begin
    J := K;
    Found := UpFound;
  end;
  if not Found or (StrLen(S) <> StrLen(Picture)) then
    Exit;

  {extract the substring}
  PTmp[0] := #0;
  W := 0;
  K := 0;
  while (UpCaseChar(Picture[J]) = Ch) and (J < StrLen(Picture)) do begin
    if S[J] <> ' ' then begin
      PTmp[k] := S[J];
      Inc(K);
      PTmp[k] := #0;
    end;
    Inc(J);
    Inc(W);
  end;

  if StrLen(PTmp) = 0 then
    I := Blank
  else if Ch = pmMonthNameU then begin
    I := MonthPCharToMonth(PTmp, W);
    if I = 0 then
      I := -1;
  end else begin
    {convert to a value}
    Val(PTmp, I, Code);
    if Code <> 0 then
      I := -1;
  end;
end;

function TOvcIntlSup.isMaskCharCount(P : PAnsiChar; MC : AnsiChar) : Word;
  {-return the number of mask characters (MC) in P}
var
  I : Cardinal;
begin
  if StrChPos(P, MC, I) then begin
    Result := 1;
    while P[I+Result] = MC do
      Inc(Result);
  end else
    Result := 0;
end;

procedure TOvcIntlSup.isMergePictureSt(Picture, P : PAnsiChar; MC : AnsiChar; SP : PAnsiChar);
var
  I, J : Cardinal;
begin
  if not StrChPos(Picture, MC, I) then
    Exit;
  J := 0;
  while Picture[I] = MC do begin
    if SP[J] = #0 then
      P[I] := ' '
    else begin
      P[I] := SP[J];
      Inc(J);
    end;
    Inc(I);
  end;
end;

procedure TOvcIntlSup.isMergeIntoPicture(Picture : PAnsiChar; Ch : AnsiChar;
                                       I : Integer);
  {-merge I into location in Picture indicated by format character Ch}
var
  Tmp  : string[MaxDateLen];
  TLen : Byte absolute Tmp;
  J    : Cardinal;
  K, L : Word;
  UCh, CPJ, CTI : AnsiChar;
  Done : Boolean;
begin
  {find the start of the subfield}
  UCh := UpCaseChar(Ch);
  if not StrChPos(Picture, Ch, J) then
    if not StrChPos(Picture, UCh, J) then
      Exit;

  {find the end of the subfield}
  K := J;
  while (J < StrLen(Picture)) and (UpCaseChar(Picture[J]) = UCh) do
    Inc(J);
  Dec(J);

  if (UCh = pmWeekDayU) or (UCh = pmMonthNameU) then begin
    if UCh = pmWeekDayU then
      case I of
        Ord(Sunday)..Ord(Saturday) :
          Tmp := LongDayNames[I+1];
        else
          Tmp := '';
      end
    else
      case I of
        1..12 :
          Tmp := LongMonthNames[I];
        else
          Tmp := '';
      end;
    K := Succ(J-K);
    if K > TLen then
      FillChar(Tmp[TLen+1], K-TLen, ' ');
    TLen := K;
  end else
    {convert I to a string}
    Str(I:MaxDateLen, Tmp);

  {now merge}
  L := TLen;
  Done := False;
  CPJ := Picture[J];

  while (UpCaseChar(CPJ) = UCh) and not Done do begin
    CTI := Tmp[L];
    if (UCh = pmMonthNameU) or (UCh = pmWeekDayU) then begin
      case CPJ of
        pmMonthNameU, pmWeekDayU :
          CTI := UpCaseChar(CTI);
      end;
    end
    {change spaces to 0's if desired}
    else if (CPJ >= 'a') and (CTI = ' ') then
      CTI := '0';
    Picture[J] := CTI;
    Done := (J = 0) or (L = 0);
    if not Done then begin
      Dec(J);
      Dec(L);
    end;
    CPJ := Picture[J];
  end;
end;

procedure TOvcIntlSup.isPackResult(Picture, S : PAnsiChar);
  {-remove unnecessary blanks from S}
var
  Temp : array[0..80] of AnsiChar;
  I, J : Integer;
begin
  FillChar(Temp, SizeOf(Temp), #0);
  I := 0;
  J := 0;
  while Picture[I] <> #0 do begin
    case Picture[I] of
      pmMonthU, pmDayU, pmMonthName, pmMonthNameU, pmWeekDay,
      pmWeekDayU, pmHourU, {pmMinU,} pmSecondU :
        if S[I] <> ' ' then begin
          Temp[J] := S[I];
          Inc(J);
        end;
      pmAmPm :
        if S[I] <> ' ' then begin
          Temp[J] := S[I];
          Inc(J);
        end
        else if (I > 0) and (Picture[I-1] = ' ') then begin
          Dec(J);
          Temp[J] := #0;
        end;
    else
      Temp[J] := S[I];
      Inc(J);
    end;
    Inc(I);
  end;

  StrCopy(S, Temp);
end;

procedure TOvcIntlSup.isSubstChar(Picture : PAnsiChar; OldCh, NewCh : AnsiChar);
  {-replace all instances of OldCh in Picture with NewCh}
var
  I : Byte;
  UpCh : AnsiChar;
  Temp : Cardinal;
begin
  UpCh := UpCaseChar(OldCh);
  if StrChPos(Picture, OldCh, Temp) or
     StrChPos(Picture, UpCh, Temp) then
    for I := 0 to StrLen(Picture)-1 do
      if UpCaseChar(Picture[I]) = UpCh then
        Picture[I] := NewCh;
end;

procedure TOvcIntlSup.isSubstCharSim(P : PAnsiChar; OC, NC : AnsiChar);
begin
  while P^ <> #0 do begin
    if P^ = OC then
      P^ := NC;
    Inc(P);
  end;
end;

function TOvcIntlSup.isTimeToTimeStringPrim(Dest, Picture : PAnsiChar;
                     T : TStTime; Pack : Boolean;
                     t1159, t2359 : PAnsiChar) : PAnsiChar;
  {-convert T to a string of the form indicated by Picture}
var
  I       : Word;
  Hours   : Byte;
  Minutes : Byte;
  Seconds : Byte;
  P       : PAnsiChar;
  TPos    : Cardinal;
  Found   : Boolean;
begin
  {merge the hours, minutes, and seconds into the picture}
  StTimeToHMS(T, Hours, Minutes, Seconds);
  StrCopy(Dest, Picture);

  P := nil;

  {check for TimeOnly}
  Found := StrChPos(Dest, pmAmPm, TPos);
  if Found then begin
    if (Hours >= 12) then
      P := t2359
    else
      P := t1159;
    if (t1159[0] <> #0) and (t2359[0] <> #0) then begin
      {adjust hours}
      case Hours of
        0      : Hours := 12;
        13..23 : Dec(Hours, 12);
      end;
    end;
  end;

  if T = BadTime then begin
    {map picture characters to spaces}
    isSubstChar(Dest, pmHour, ' ');
    isSubstChar(Dest, pmMinute, ' ');
    isSubstChar(Dest, pmSecond, ' ');
  end else begin
    {merge the numbers into the picture}
    isMergeIntoPicture(Dest, pmHour, Hours);
    isMergeIntoPicture(Dest, pmMinute, Minutes);
    isMergeIntoPicture(Dest, pmSecond, Seconds);
  end;

  {map colons}
  isSubstChar(Dest, pmTimeColon, wColonChar);

  {plug in AM/PM string if appropriate}
  if Found then begin
    if (t1159[0] = #0) and (t2359[0] = #0) then
      isSubstCharSim(@Dest[TPos], pmAmPm, ' ')
    else if (T = BadTime) and (t1159[0] = #0) then
      isSubstCharSim(@Dest[TPos], pmAmPm, ' ')
    else begin
      I := 0;
      while (Dest[TPos] = pmAmPm) and (P[I] <> #0) do begin
        Dest[TPos] := P[I];
        Inc(I);
        Inc(TPos);
      end;
    end;
  end;

  if Pack and (T <> BadTime) then
    isPackResult(Picture, Dest);

  Result := Dest;
end;

function TOvcIntlSup.MonthStringToMonth(const S : string; Width : Byte) : Byte;
  {-Convert the month name in MSt to a month (1..12)}
var
  I    : Word;
  Mt   : string[MaxDateLen];
  MLen : Byte absolute Mt;
  St   : string[MaxDateLen];
  SLen : Byte absolute St;
begin
  Result := 0;
  Mt := AnsiUpperCase(S);
  if Width > MLen then
    FillChar(Mt[MLen+1], Width-MLen, ' ');
  MLen := Width;

  for I := 1 to 12 do begin
    St := AnsiUpperCase(LongMonthNames[I]);
    if Width > SLen then
      FillChar(St[SLen+1], Width-SLen, ' ');
    SLen := Width;
    if Mt = St then begin
      Result := I;
      Break;
    end;
  end;
end;

function TOvcIntlSup.MonthPCharToMonth(S : PAnsiChar; Width : Byte) : Byte;
  {-convert the month name in S to a month (1..12)}
var
  I    : Word;
  Mt   : string[MaxDateLen];
  MLen : Byte absolute Mt;
  St   : string[MaxDateLen];
  SLen : Byte absolute St;
begin
  Result := 0;
  Mt := AnsiUpperCase(StrPas(S));
  if Width > MLen then
    FillChar(Mt[MLen+1], Width-MLen, ' ');
  MLen := Width;

  for I := 1 to 12 do begin
    St := AnsiUpperCase(LongMonthNames[I]);
    if Width > SLen then
      FillChar(St[SLen+1], Width-SLen, ' ');
    SLen := Width;
    if Mt = St then begin
      Result := I;
      Break;
    end;
  end;
end;

function TOvcIntlSup.MonthToString(Month : Integer) : string;
  {-return month name as a string for Month}
begin
  if (Month >= 1) and (Month <= 12) then
    Result := LongMonthNames[Month]
  else
    Result := '';
end;

function TOvcIntlSup.MonthToPChar(Dest : PAnsiChar; Month : Integer) : PAnsiChar;
  {-return month name as a string for Month}
begin
  Result := Dest;
  if (Month >= 1) and (Month <= 12) then
    StrPCopy(Dest, LongMonthNames[Month])
  else
    Dest[0] := #0;
end;

procedure TOvcIntlSup.ResetInternationalInfo;
  {-read Window's international information and string resources}
var
  S    : string;
  I    : Cardinal;
  Buf  : array[0..255] of AnsiChar;
  R    : TRegistry;

  procedure GetIntlString(S, Def, Buf : PAnsiChar; Size : Word);
  begin
    GetProfileString('intl', S, Def, Buf, Size);
  end;

  function GetIntlChar(S, Def : PAnsiChar) : AnsiChar;
  var
    B : array[0..5] of AnsiChar;
  begin
    GetIntlString(S, Def, B, SizeOf(B));
    Result := B[0];
    if (Result = #0) then
      Result := Def[0];
  end;

  procedure ExtractSubString(SubChar : AnsiChar; Dest : PAnsiChar);
  var
    I, Temp : Cardinal;
    L       : Word;
  begin
    FillChar(Dest^, SizeOf(wldSub1), 0);
    if not StrChPos(wLongDate, '''', I) then
      Exit;

    {delete the first quote}
    StrChDeletePrim(wLongDate, I);

    {assure that there is another quote}
    if not StrChPos(wLongDate, '''', Temp) then
      Exit;

    {copy substring into Dest, replace substring with SubChar}
    L := 0;
    while wLongDate[I] <> '''' do
      if L < SizeOf(wldSub1) then begin
        Dest[L] := wLongDate[I];
        Inc(L);
        wLongDate[I] := SubChar;
        Inc(I);
      end else
        StrChDeletePrim(wLongDate, I);

    {delete the second quote}
    StrChDeletePrim(wLongDate, I);
  end;

begin
  FDecimalChar     := GetIntlChar('sDecimal',
    @DefaultIntlData.DecimalChar);
  FCommaChar       := GetIntlChar('sThousand',
    @DefaultIntlData.CommaChar);
  FCurrencyDigits  := GetProfileInt('intl', 'iCurrDigits',
    DefaultIntlData.CurrDigits);
  if (FCommaChar = FDecimalChar) then begin
    FDecimalChar := DefaultIntlData.DecimalChar;
    FCommaChar := DefaultIntlData.CommaChar;
  end;
  wNegCurrencyForm := GetProfileInt('intl', 'iNegCurr', 0);
  FListChar        := GetIntlChar('sList', ',');

  GetIntlString('sCountry', '', Buf, SizeOf(Buf));
  wCountry := StrNew(Buf);

  GetIntlString('sCurrency', DefaultIntlData.CurrencyLtStr,
    FCurrencyLtStr, SizeOf(FCurrencyLtStr));
  StrCopy(FCurrencyRtStr, FCurrencyLtStr);

  wCurrencyForm := GetProfileInt('intl', 'iCurrency', 0);
  case wCurrencyForm of
    0 : {};
    1 : {};
    2 : StrCat(FCurrencyLtStr, ' ');
    3 : StrChInsertPrim(FCurrencyRtStr, ' ', 0);
  end;

  wTLZero := GetProfileInt('intl', 'iTLZero', 0) <> 0;
  w12Hour := LongTimeFormat[Length(LongTimeFormat)] = 'M';

  wColonChar := GetIntlChar('sTTime', ':');
  FSlashChar := GetIntlChar('sDate', @DefaultIntlData.SlashChar);
  GetIntlString('s1159', 'AM', w1159, SizeOf(w1159));
  GetIntlString('s2359', 'PM', w2359, SizeOf(w2359));

  {get short date mask and fix it up}
{$IFDEF MSWINDOWS}
     R := TRegistry.Create;
     try
       R.RootKey := HKEY_CURRENT_USER;
       if R.OpenKey('Control Panel\International', False) then begin
         try
           if R.ValueExists('sShortDate') then
             StrPCopy(wShortDate, R.ReadString('sShortDate'))
           else
             GetIntlString('sShortDate', 'MM/dd/yy',
                            wShortDate, SizeOf(wShortDate));
         finally
           R.CloseKey;
         end;
       end else
         GetIntlString('sShortDate', 'MM/dd/yy',
                        wShortDate, SizeOf(wShortDate));
     finally
       R.Free;
     end;
{$ELSE}
         GetIntlString('sShortDate', 'MM/dd/yy',
                        wShortDate, SizeOf(wShortDate));
{$ENDIF}

  I := 0;
  while wShortDate[I] <> #0 do begin
    if wShortDate[I] = SlashChar then
      wShortDate[I] := '/';
    Inc(I);
  end;

  {get long date mask and fix it up}
  GetIntlString('sLongDate',  'dddd, MMMM dd, yyyy',
                wLongDate,  SizeOf(wLongDate));
  ExtractSubString(pmLongDateSub1, wldSub1);
  ExtractSubString(pmLongDateSub2, wldSub2);
  ExtractSubString(pmLongDateSub3, wldSub3);

  {replace ddd/dddd with www/wwww}
  if StrStPos(wLongDate, 'ddd', I) then
    while wLongDate[I] = 'd' do begin
      wLongDate[I] := 'w';
      Inc(I);
    end;

  {replace MMM/MMMM with nnn/nnnn}
  if StrStPos(wShortDate, 'MMM', I) then
    while wShortDate[I] = 'M' do begin
      wShortDate[I] := 'n';
      Inc(I);
    end;

  {replace MMM/MMMM with nnn/nnnn}
  if StrStPos(wLongDate, 'MMM', I) then
    while wLongDate[I] = 'M' do begin
      wLongDate[I] := 'n';
      Inc(I);
    end;

  {deal with oddities concerning . and ,}
  I := 0;
  while wLongDate[I] <> #0 do begin
    case wLongDate[I] of
      '.', ',' :
        if wLongDate[I+1] <> ' ' then begin
          StrChInsertPrim(wLongDate, ' ', I+1);
          Inc(I);
        end;
    end;
    Inc(I);
  end;

  {get Y/N and T/F values}
  S := GetOrphStr(SCYes);
  if Length(S) = 1 then
    YesChar := S[1];
  S := GetOrphStr(SCNo);
  if Length(S) = 1 then
    NoChar := S[1];
  S := GetOrphStr(SCTrue);
  if Length(S) = 1 then
    TrueChar := S[1];
  S := GetOrphStr(SCFalse);
  if Length(S) = 1 then
    FalseChar := S[1];
end;

procedure TOvcIntlSup.SetAutoUpdate(Value : Boolean);
  {-set the AutoUpdate option}
begin
  if Value <> FAutoUpdate then begin
    FAutoUpdate := Value;
// AllocateHWnd not available in LCL to create non-visual window that
// responds to messages sent to control. But not needed?
{$IFNDEF LCL}
    if FAutoUpdate then
      {allocate our window handle}
      {$IFDEF VERSION6}
        intlHandle := Classes.AllocateHWnd(isIntlWndProc)
      {$ELSE}
        intlHandle := AllocateHWnd(isIntlWndProc)
      {$ENDIF}
    else begin
      {deallocate our window handle}
      if intlHandle <> 0 then
        {$IFDEF VERSION6}
          Classes.DeallocateHWnd(intlHandle);
        {$ELSE}
          DeallocateHWnd(intlHandle);
        {$ENDIF}
      intlHandle := 0;
    end;
{$ENDIF}
  end;
end;

procedure TOvcIntlSup.SetCurrencyLtStr(const Value : string);
begin
  StrPLCopy(FCurrencyLtStr, Value, SizeOf(FCurrencyLtStr)-1);
end;

procedure TOvcIntlSup.SetCurrencyRtStr(const Value : string);
begin
  StrPLCopy(FCurrencyRtStr, Value, SizeOf(FCurrencyRtStr)-1);
end;

function TOvcIntlSup.TimeStringToHMS(const Picture, S : string;
         var Hour, Minute, Second : Integer) : Boolean;
  {-extract Hours, Minutes, Seconds from St, returning true if string is valid}
var
  Buf1 : array[0..255] of AnsiChar;
  Buf2 : array[0..255] of AnsiChar;
begin
  StrPCopy(Buf1, Picture);
  StrPCopy(Buf2, S);
  Result := TimePCharToHMS(Buf1, Buf2, Hour, Minute, Second);
end;

function TOvcIntlSup.TimePCharToHMS(Picture, S : PAnsiChar;
         var Hour, Minute, Second : Integer) : Boolean;
  {-extract Hours, Minutes, Seconds from St, returning true if string is valid}
var
  I, J  : Cardinal;
  Tmp,
  t1159,
  t2359 : array[0..20] of AnsiChar;
begin
  Result := False;
  if StrLen(Picture) <> StrLen(S) then
    Exit;

  {extract hours, minutes, seconds from St}
  isExtractFromPicture(Picture, S, pmHour, Hour, -1, 0);
  isExtractFromPicture(Picture, S, pmMinute,  Minute, -1, 0);
  isExtractFromPicture(Picture, S, pmSecond,  Second, -1, 0);
  if (Hour = -1) or (Minute = -1) or (Second = -1) then begin
    Result := False;
    Exit;
  end;

  {check for TimeOnly}
  if StrChPos(Picture, pmAmPm, I) and (w1159[0] <> #0)
    and (w2359[0] <> #0) then begin
    Tmp[0] := #0;
    J := 0;
    while Picture[I] = pmAmPm do begin
      Tmp[J] := S[I];
      Inc(J);
      Inc(I);
    end;
    Tmp[J] := #0;
    TrimTrailPrimPChar(Tmp);

    StrCopy(t1159, w1159);
    t1159[J] := #0;
    StrCopy(t2359, w2359);
    t2359[J] := #0;

    if (Tmp[0] = #0) then
      Hour := -1
    else if StrIComp(Tmp, t2359) = 0 then begin
      if (Hour < 12) then
        Inc(Hour, 12)
      else if (Hour = 0) or (Hour > 12) then
        {force BadTime}
        Hour := -1;
    end else if StrIComp(Tmp, t1159) = 0 then begin
      if Hour = 12 then
        Hour := 0
      else if (Hour = 0) or (Hour > 12) then
        {force BadTime}
        Hour := -1;
    end else
      {force BadTime}
      Hour := -1;
  end;

  Result := ValidTime(Hour, Minute, Second);
end;

function TOvcIntlSup.TimeToAmPmString(const Picture : string; T : TStTime; Pack : Boolean) : string;
  {-convert T to a string of the form indicated by Picture. Times are always displayed in am/pm format.}
var
  Buf1 : array[0..255] of AnsiChar;
  Buf2 : array[0..255] of AnsiChar;
begin
  StrPCopy(Buf1, Picture);
  Result := StrPas(TimeToAmPmPChar(Buf2, Buf1, T, Pack));
end;

function TOvcIntlSup.TimeToAmPmPChar(Dest : PAnsiChar; Picture : PAnsiChar; T : TStTime; Pack : Boolean) : PAnsiChar;
  {-convert T to a string of the form indicated by Picture. Times are always displayed in am/pm format.}
const
  t1159 = 'AM'#0;
  t2359 = 'PM'#0;
var
  PLen : Byte;
  Temp : Cardinal;
begin
  Move(Picture[0], Dest[0], StrLen(Picture)+1);
  if not StrChPos(Dest, pmAmPm, Temp) then begin
    PLen := StrLen(Dest);
    Dest[PLen] := pmAmPm;
    Dest[PLen+1] := #0;
  end;
  Result := isTimeToTimeStringPrim(Dest, Dest, T, Pack, t1159, t2359);
end;

function TOvcIntlSup.TimeStringToTime(const Picture, S : string) : TStTime;
  {-convert S, a string of the form indicated by Picture, to a Time variable}
var
  Buf1 : array[0..255] of AnsiChar;
  Buf2 : array[0..255] of AnsiChar;
begin
  StrPCopy(Buf1, Picture);
  StrPCopy(Buf2, S);
  Result := TimePCharToTime(Buf1, Buf2);
end;

function TOvcIntlSup.TimePCharToTime(Picture, S : PAnsiChar) : TStTime;
  {-convert S, a string of the form indicated by Picture, to a Time variable}
var
  Hours, Minutes, Seconds : Integer;
begin
  if TimePCharToHMS(Picture, S, Hours, Minutes, Seconds) then
    Result := HMStoStTime(Hours, Minutes, Seconds)
  else
    Result := BadTime;
end;

function TOvcIntlSup.TimeToTimeString(const Picture : string; T : TStTime; Pack : Boolean) : string;
  {-convert T to a string of the form indicated by Picture}
var
  Buf1 : array[0..255] of AnsiChar;
  Buf2 : array[0..255] of AnsiChar;
begin
  StrPCopy(Buf1, Picture);
  Result := StrPas(TimeToTimePChar(Buf2, Buf1, T, Pack));
end;

function TOvcIntlSup.TimeToTimePChar(Dest : PAnsiChar; Picture : PAnsiChar; T : TStTime; Pack : Boolean) : PAnsiChar;
  {-convert T to a string of the form indicated by Picture}
begin
  Result := isTimeToTimeStringPrim(Dest, Picture, T, Pack, w1159, w2359);
end;

procedure DestroyGlobalIntlSup; far;
begin
  OvcIntlSup.Free;
end;


initialization
  {create instance of default user data class}
  OvcIntlSup := TOvcIntlSup.Create;

finalization
  DestroyGlobalIntlSup;
end.
