[KERNEL32] Era first year is GANNEN
[reactos.git] / dll / win32 / kernel32 / winnls / string / japanese.c
1 /*
2 * PROJECT: ReactOS system libraries
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Japanese era support
5 * COPYRIGHT: Copyright 2019 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
6 */
7 #include <k32.h>
8
9 #define NDEBUG
10 #include <debug.h>
11 #include "japanese.h"
12
13 #define JAPANESE_ERA_MAX 16
14
15 /* #define DONT_USE_REGISTRY */
16
17 static DWORD s_JapaneseEraCount = 0;
18 static JAPANESE_ERA s_JapaneseEraTable[JAPANESE_ERA_MAX]
19 #ifdef DONT_USE_REGISTRY
20 =
21 {
22 {1868, 1, 1, {0x660E, 0x6CBB}, {0x660E, 0}, L"Meiji", L"M"},
23 {1912, 7, 30, {0x5927, 0x6B63}, {0x5927, 0}, L"Taisho", L"T"},
24 {1926, 12, 25, {0x662D, 0x548C}, {0x662D, 0}, L"Showa", L"S"},
25 {1989, 1, 8, {0x5E73, 0x6210}, {0x5E73, 0}, L"Heisei", L"H"},
26 {2019, 5, 1, {0x4EE4, 0x548C}, {0x4EE4, 0}, L"Reiwa", L"R"},
27 }
28 #endif
29 ;
30
31 HANDLE NLS_RegOpenKey(HANDLE hRootKey, LPCWSTR szKeyName);
32
33 BOOL NLS_RegEnumValue(HANDLE hKey, UINT ulIndex,
34 LPWSTR szValueName, ULONG valueNameSize,
35 LPWSTR szValueData, ULONG valueDataSize);
36
37 static INT JapaneseEra_Compare(const void *e1, const void *e2)
38 {
39 PCJAPANESE_ERA pEra1 = (PCJAPANESE_ERA)e1;
40 PCJAPANESE_ERA pEra2 = (PCJAPANESE_ERA)e2;
41 if (pEra1->wYear < pEra2->wYear)
42 return -1;
43 if (pEra1->wYear > pEra2->wYear)
44 return 1;
45 if (pEra1->wMonth < pEra2->wMonth)
46 return -1;
47 if (pEra1->wMonth > pEra2->wMonth)
48 return 1;
49 if (pEra1->wDay < pEra2->wDay)
50 return -1;
51 if (pEra1->wDay > pEra2->wDay)
52 return 1;
53 return 0;
54 }
55
56 BOOL JapaneseEra_IsFirstYearGannen(void)
57 {
58 #ifdef DONT_USE_REGISTRY
59 return TRUE;
60 #else
61 HANDLE KeyHandle;
62 DWORD dwIndex;
63 WCHAR szName[32], szValue[32];
64 static BOOL s_bIsCached = FALSE, s_bFirstIsGannen = TRUE;
65
66 if (s_bIsCached)
67 return s_bFirstIsGannen;
68
69 KeyHandle = NLS_RegOpenKey(NULL, L"\\Registry\\Machine\\System\\"
70 L"CurrentControlSet\\Control\\Nls\\Calendars\\Japanese");
71 if (!KeyHandle)
72 return TRUE;
73
74 for (dwIndex = 0; dwIndex < 16; ++dwIndex)
75 {
76 if (!NLS_RegEnumValue(KeyHandle, dwIndex, szName, sizeof(szName),
77 szValue, sizeof(szValue)))
78 {
79 break;
80 }
81
82 if (lstrcmpiW(szName, L"InitialEraYear") == 0)
83 {
84 s_bFirstIsGannen = (szValue[0] == 0x5143);
85 s_bIsCached = TRUE;
86 break;
87 }
88 }
89
90 return s_bFirstIsGannen;
91 #endif
92 }
93
94 /*
95 * SEE ALSO:
96 * https://en.wikipedia.org/wiki/Japanese_era_name
97 * https://docs.microsoft.com/en-us/windows/desktop/Intl/era-handling-for-the-japanese-calendar
98 */
99 static PCJAPANESE_ERA JapaneseEra_Load(DWORD *pdwCount)
100 {
101 #ifndef DONT_USE_REGISTRY
102 HANDLE KeyHandle;
103 DWORD dwIndex;
104 WCHAR szName[128], szValue[128];
105 JAPANESE_ERA *pEntry;
106 LPWSTR pch1, pch2, pch3, pch4;
107 #endif
108
109 ASSERT(pdwCount != NULL);
110
111 /* return cache if any */
112 if (s_JapaneseEraCount != 0)
113 {
114 *pdwCount = s_JapaneseEraCount;
115 return s_JapaneseEraTable;
116 }
117
118 #ifdef DONT_USE_REGISTRY
119 s_JapaneseEraCount = ARRAYSIZE(s_JapaneseEraTable);
120 #else
121 /* init */
122 *pdwCount = 0;
123 RtlZeroMemory(&s_JapaneseEraTable, sizeof(s_JapaneseEraTable));
124
125 /* open registry key */
126 KeyHandle = NLS_RegOpenKey(NULL, L"\\Registry\\Machine\\System\\"
127 L"CurrentControlSet\\Control\\Nls\\Calendars\\Japanese\\Eras");
128 if (!KeyHandle)
129 return NULL;
130
131 /* for all values */
132 for (dwIndex = 0; dwIndex < JAPANESE_ERA_MAX; ++dwIndex)
133 {
134 pEntry = &s_JapaneseEraTable[dwIndex];
135
136 /* get name and data */
137 if (!NLS_RegEnumValue(KeyHandle, dwIndex, szName, sizeof(szName),
138 szValue, sizeof(szValue)))
139 {
140 break;
141 }
142
143 /* split fields */
144 pch1 = szName;
145 pch2 = wcschr(pch1, L' ');
146 if (pch2 == NULL)
147 {
148 break;
149 }
150 *pch2++ = UNICODE_NULL;
151
152 pch3 = wcschr(pch2, L' ');
153 if (pch3 == NULL)
154 {
155 break;
156 }
157 *pch3++ = UNICODE_NULL;
158
159 pEntry->wYear = _wtoi(pch1);
160 pEntry->wMonth = _wtoi(pch2);
161 pEntry->wDay = _wtoi(pch3);
162 if (pEntry->wYear == 0 || pEntry->wMonth == 0 || pEntry->wDay == 0)
163 {
164 break;
165 }
166
167 /* split fields */
168 pch1 = szValue;
169 pch2 = wcschr(pch1, L'_');
170 if (pch2 == NULL)
171 {
172 break;
173 }
174 *pch2++ = UNICODE_NULL;
175
176 pch3 = wcschr(pch2, L'_');
177 if (pch3 == NULL)
178 {
179 break;
180 }
181 *pch3++ = UNICODE_NULL;
182
183 pch4 = wcschr(pch3, L'_');
184 if (pch4 == NULL)
185 {
186 break;
187 }
188 *pch4++ = UNICODE_NULL;
189
190 /* store */
191 RtlStringCbCopyW(pEntry->szEraName, sizeof(pEntry->szEraName), pch1);
192 RtlStringCbCopyW(pEntry->szEraAbbrev, sizeof(pEntry->szEraAbbrev), pch2);
193 RtlStringCbCopyW(pEntry->szEnglishEraName, sizeof(pEntry->szEnglishEraName), pch3);
194 RtlStringCbCopyW(pEntry->szEnglishEraAbbrev, sizeof(pEntry->szEnglishEraAbbrev), pch4);
195 }
196
197 /* close key */
198 NtClose(KeyHandle);
199
200 /* sort */
201 qsort(s_JapaneseEraTable, s_JapaneseEraCount, sizeof(JAPANESE_ERA),
202 JapaneseEra_Compare);
203
204 /* make cache */
205 s_JapaneseEraCount = dwIndex;
206 #endif
207
208 *pdwCount = s_JapaneseEraCount;
209
210 return s_JapaneseEraTable;
211 }
212
213 static BOOL JapaneseEra_ToSystemTime(PCJAPANESE_ERA pEra, LPSYSTEMTIME pst)
214 {
215 ASSERT(pEra != NULL);
216 ASSERT(pst != NULL);
217
218 ZeroMemory(pst, sizeof(*pst));
219 pst->wYear = pEra->wYear;
220 pst->wMonth = pEra->wMonth;
221 pst->wDay = pEra->wDay;
222 return TRUE;
223 }
224
225 PCJAPANESE_ERA JapaneseEra_Find(const SYSTEMTIME *pst OPTIONAL)
226 {
227 DWORD dwIndex, dwCount = 0;
228 PCJAPANESE_ERA pTable, pEntry, pPrevEntry = NULL;
229 SYSTEMTIME st1, st2;
230 FILETIME ft1, ft2;
231 LONG nCompare;
232
233 /* pst --> ft1 */
234 if (pst == NULL)
235 {
236 GetLocalTime(&st1);
237 pst = &st1;
238 }
239 SystemTimeToFileTime(pst, &ft1);
240
241 /* load era table */
242 pTable = JapaneseEra_Load(&dwCount);
243 if (pTable == NULL || dwCount == 0 || dwCount > JAPANESE_ERA_MAX)
244 {
245 return NULL;
246 }
247
248 /* for all eras */
249 for (dwIndex = 0; dwIndex < dwCount; dwIndex++)
250 {
251 pEntry = &pTable[dwIndex];
252
253 /* pEntry --> st2 --> ft2 */
254 JapaneseEra_ToSystemTime(pEntry, &st2);
255 SystemTimeToFileTime(&st2, &ft2);
256
257 /* ft1 <=> ft2 */
258 nCompare = CompareFileTime(&ft1, &ft2);
259 if (nCompare == 0)
260 return pEntry;
261 if (nCompare < 0)
262 return pPrevEntry;
263 pPrevEntry = pEntry;
264 }
265
266 return pPrevEntry;
267 }