-
Notifications
You must be signed in to change notification settings - Fork 3
/
cbrvalutes.pas
243 lines (210 loc) · 6.96 KB
/
cbrvalutes.pas
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
unit cbrvalutes;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, fgl
;
type
TValuteCurrency = (vcUnknown, vcRUB, vcUSD, vcEUR, vcGBP, vcKZT, vcTRY, vcJPY, vcCNY, vcTJS, vcUZS, vcUAH, vcKGS,
vcHKD);
{ TValuteItem }
TValuteItem = class
private
FCharCode: String;
FID: String;
FName: String;
FNominal: Word;
FNumCode: Word;
FPrevious: Double;
FValue: Double;
published
property ID: String read FID write FID;
property NumCode: Word read FNumCode write FNumCode;
property CharCode: String read FCharCode write FCharCode;
property Nominal: Word read FNominal write FNominal;
property Name: String read FName write FName;
property Value: Double read FValue write FValue;
property Previous: Double read FPrevious write FPrevious;
end;
TValuteItemList = specialize TFPGObjectList<TValuteItem>;
{ TValuteList }
TValuteList = class
private
FValuteItems: TValuteItemList;
function GetRUB: TValuteItem;
function GetValutes(Index: TValuteCurrency): TValuteItem;
public
constructor Create;
destructor Destroy; override;
property Valutes[Index: TValuteCurrency]: TValuteItem read GetValutes; default;
property RUB: TValuteItem read GetRUB;
published
property USD: TValuteItem index vcUSD read GetValutes;
property EUR: TValuteItem index vcEUR read GetValutes;
property GBP: TValuteItem index vcGBP read GetValutes;
property KZT: TValuteItem index vcKZT read GetValutes;
property &TRY: TValuteItem index vcTRY read GetValutes;
property JPY: TValuteItem index vcJPY read GetValutes;
property CNY: TValuteItem index vcCNY read GetValutes;
property TJS: TValuteItem index vcTJS read GetValutes;
property UZS: TValuteItem index vcUZS read GetValutes;
property UAH: TValuteItem index vcUAH read GetValutes;
property KGS: TValuteItem index vcKGS read GetValutes;
property HKD: TValuteItem index vcHKD read GetValutes;
end;
{ TDailyValutes }
TDailyValutes = class
private
FDate: String;
FPreviousDate: String;
FPreviousURL: String;
FTimeStamp: String;
FValute: TValuteList;
FLastUpdate: TDateTime;
public
constructor Create;
destructor Destroy; override;
function IsRelevant: Boolean;
procedure CheckRelevance;
procedure UpdateData;
function CalcRate(InCurrency, OutCurrency: TValuteCurrency): Double;
published
property Date: String read FDate write FDate;
property PreviousDate: String read FPreviousDate write FPreviousDate;
property PreviousURL: String read FPreviousURL write FPreviousURL;
property Timestamp: String read FTimeStamp write FTimestamp;
property Valute: TValuteList read FValute write FValute;
end;
var
_CBRDailyValutes: TDailyValutes;
CriticalSectionCBR: TRTLCriticalSection;
function StrToValuteCurrency(const aValute: String): TValuteCurrency;
implementation
uses
DateUtils, fphttpclient, fpjsonrtti, jsonparser, jsonscanner
;
function StrToValuteCurrency(const aValute: String): TValuteCurrency;
begin
case AnsiLowerCase(aValute) of
'rub', 'руб', 'р', '₽', 'rubles', 'ruble', 'рублей', 'рубль', 'рубля', 'рубли': Result:=vcRUB;
'usd', 'долларов', '$', 'dollars', 'dollar', 'доллар': Result:=vcUSD;
'eur', 'euro', 'евро', '€': Result:=vcEUR;
'gbp', 'фунт', '£': Result:=vcGBP;
'kzt', 'тенге', '₸': Result:=vcKZT;
'try', 'лира', 'лир': Result:=vcTRY;
'jpy', 'иена', '¥', 'иен': Result:=vcJPY;
'cny', 'юань', 'юаней', 'rmb', 'юани', '元': Result:=vcCNY;
'tjs', 'сомони', 'сомани': Result:=vcTJS;
'uzs', 'сум': Result:=vcUZS;
'uah', 'гривны', 'гривна', 'гривен', '₴': Result:=vcUAH;
'kgs', 'сом', 'сомов', 'сома': Result:=vcKGS;
'hkd': Result:=vcHKD;
else
Result:=vcUnknown;
end;
end;
{ TValuteList }
function TValuteList.GetValutes(Index: TValuteCurrency): TValuteItem;
var
aIndex: Integer;
begin
if Index=vcRUB then
Exit(GetRUB);
aIndex:=Ord(Index);
if not Assigned(FValuteItems[aIndex]) then
FValuteItems[aIndex]:=TValuteItem.Create;
Result:=FValuteItems[aIndex];
end;
function TValuteList.GetRUB: TValuteItem;
begin
if not Assigned(FValuteItems[Ord(vcRUB)]) then
begin
FValuteItems[Ord(vcRUB)]:=TValuteItem.Create;
with FValuteItems[Ord(vcRUB)] do
begin
ID:='R00000'; // Какой правильный ID для рубля?
NumCode:=643;
CharCode:='RUB';
Nominal:=1;
Name:='Российский рубль';
Value:=1;
Previous:=1;
end;
end;
Result:=FValuteItems[Ord(vcRUB)];
end;
constructor TValuteList.Create;
begin
FValuteItems:=TValuteItemList.Create(True);
FValuteItems.Count:=ord(High(TValuteCurrency)) + 1;
end;
destructor TValuteList.Destroy;
begin
FValuteItems.Free;
inherited Destroy;
end;
{ TDailyValutes }
constructor TDailyValutes.Create;
begin
FValute:=TValuteList.Create;
FLastUpdate:=Now-2;
InitCriticalSection(CriticalSectionCBR);
end;
destructor TDailyValutes.Destroy;
begin
DoneCriticalSection(CriticalSectionCBR);
FValute.Free;
inherited Destroy;
end;
function TDailyValutes.IsRelevant: Boolean;
begin
Result:=Now-FLastUpdate<1/6; // Меньше 4 часов
end;
procedure TDailyValutes.CheckRelevance;
begin
if not IsRelevant then
UpdateData;
end;
procedure TDailyValutes.UpdateData;
var
DeStreamer: TJSONDeStreamer;
aJSON: String;
const
CBR_ENDPOINT='https://www.cbr-xml-daily.ru/daily_json.js';
begin
try
aJSON:=TFPHTTPClient.SimpleGet(CBR_ENDPOINT);
except
aJSON:=EmptyStr; // to-do
end;
if aJSON=EmptyStr then
Exit;
DeStreamer := TJSONDeStreamer.Create(nil);
EnterCriticalSection(CriticalSectionCBR);
try
try
DeStreamer.JSONToObject(aJSON, self);
FLastUpdate:=Now;
except
// to-do
end;
finally
LeaveCriticalSection(CriticalSectionCBR);
DeStreamer.Destroy;
end;
end;
function TDailyValutes.CalcRate(InCurrency, OutCurrency: TValuteCurrency): Double;
var
aValIn, aValOut: TValuteItem;
aRubleValue: Double;
begin
aValOut:=Valute.Valutes[OutCurrency];
aValIn :=Valute.Valutes[InCurrency];
aRubleValue:=aValIn.Value/aValIn.Nominal;
Result:=aRubleValue/aValOut.Value*aValOut.Nominal;
end;
initialization
_CBRDailyValutes:=TDailyValutes.Create;
finalization
_CBRDailyValutes.Free;
end.