@gracefullight/saju 1.3.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +8 -4
- package/README.md +8 -4
- package/dist/__tests__/date-fns-adapter.test.js +11 -0
- package/dist/__tests__/saju.test.js +22 -0
- package/dist/adapters/date-fns.d.ts +2 -2
- package/dist/adapters/date-fns.d.ts.map +1 -1
- package/dist/adapters/date-fns.js +39 -38
- package/dist/core/yongshen.d.ts.map +1 -1
- package/dist/core/yongshen.js +46 -34
- package/package.json +1 -1
package/README.ko.md
CHANGED
|
@@ -151,16 +151,20 @@ import { getFourPillars, STANDARD_PRESET } from "@gracefullight/saju";
|
|
|
151
151
|
|
|
152
152
|
const adapter = await createDateFnsAdapter();
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
timeZone: "Asia/Seoul",
|
|
157
|
-
};
|
|
154
|
+
// 일반 Date를 바로 사용할 수 있습니다
|
|
155
|
+
const dt = new Date(1985, 4, 15, 14, 30); // 주의: 월은 0부터 시작
|
|
158
156
|
|
|
159
157
|
const result = getFourPillars(dt, {
|
|
160
158
|
adapter,
|
|
161
159
|
longitudeDeg: 126.9778,
|
|
162
160
|
preset: STANDARD_PRESET,
|
|
163
161
|
});
|
|
162
|
+
|
|
163
|
+
// 명시적인 타임존 메타데이터가 필요하면 래퍼 객체도 계속 사용할 수 있습니다
|
|
164
|
+
const zonedDt = {
|
|
165
|
+
date: new Date(1985, 4, 15, 14, 30),
|
|
166
|
+
timeZone: "Asia/Seoul",
|
|
167
|
+
};
|
|
164
168
|
```
|
|
165
169
|
|
|
166
170
|
### 커스텀 날짜 어댑터
|
package/README.md
CHANGED
|
@@ -151,16 +151,20 @@ import { getFourPillars, STANDARD_PRESET } from "@gracefullight/saju";
|
|
|
151
151
|
|
|
152
152
|
const adapter = await createDateFnsAdapter();
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
timeZone: "Asia/Seoul",
|
|
157
|
-
};
|
|
154
|
+
// Plain Date works directly
|
|
155
|
+
const dt = new Date(1985, 4, 15, 14, 30); // Note: month is 0-indexed
|
|
158
156
|
|
|
159
157
|
const result = getFourPillars(dt, {
|
|
160
158
|
adapter,
|
|
161
159
|
longitudeDeg: 126.9778,
|
|
162
160
|
preset: STANDARD_PRESET,
|
|
163
161
|
});
|
|
162
|
+
|
|
163
|
+
// If you need explicit timezone metadata, you can still pass a wrapper object
|
|
164
|
+
const zonedDt = {
|
|
165
|
+
date: new Date(1985, 4, 15, 14, 30),
|
|
166
|
+
timeZone: "Asia/Seoul",
|
|
167
|
+
};
|
|
164
168
|
```
|
|
165
169
|
|
|
166
170
|
### Custom Date Adapter
|
|
@@ -6,6 +6,13 @@ describe("date-fns Adapter", () => {
|
|
|
6
6
|
adapter = await createDateFnsAdapter();
|
|
7
7
|
});
|
|
8
8
|
describe("Basic date getters", () => {
|
|
9
|
+
it("should accept plain Date values", () => {
|
|
10
|
+
const dt = new Date(2000, 0, 1, 18, 0);
|
|
11
|
+
expect(adapter.getYear(dt)).toBe(2000);
|
|
12
|
+
expect(adapter.getMonth(dt)).toBe(1);
|
|
13
|
+
expect(adapter.getDay(dt)).toBe(1);
|
|
14
|
+
expect(adapter.getHour(dt)).toBe(18);
|
|
15
|
+
});
|
|
9
16
|
it("should get year correctly", () => {
|
|
10
17
|
const dt = { date: new Date(2000, 0, 1), timeZone: "Asia/Seoul" };
|
|
11
18
|
expect(adapter.getYear(dt)).toBe(2000);
|
|
@@ -34,6 +41,10 @@ describe("date-fns Adapter", () => {
|
|
|
34
41
|
const dt = { date: new Date(2000, 0, 1), timeZone: "Asia/Seoul" };
|
|
35
42
|
expect(adapter.getZoneName(dt)).toBe("Asia/Seoul");
|
|
36
43
|
});
|
|
44
|
+
it("should fall back to system timezone for plain Date values", () => {
|
|
45
|
+
const dt = new Date(2000, 0, 1);
|
|
46
|
+
expect(adapter.getZoneName(dt)).toBe(Intl.DateTimeFormat().resolvedOptions().timeZone);
|
|
47
|
+
});
|
|
37
48
|
});
|
|
38
49
|
describe("Date arithmetic", () => {
|
|
39
50
|
it("should add minutes correctly", () => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DateTime } from "luxon";
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { createDateFnsAdapter } from "../adapters/date-fns";
|
|
3
4
|
import { createLuxonAdapter } from "../adapters/luxon";
|
|
4
5
|
import { getSaju, STANDARD_PRESET } from "../index";
|
|
5
6
|
describe("getSaju integration", () => {
|
|
@@ -140,4 +141,25 @@ describe("getSaju integration", () => {
|
|
|
140
141
|
expect(result.solarTerms.prevJieDate).toBeDefined();
|
|
141
142
|
expect(result.solarTerms.nextJieDate).toBeDefined();
|
|
142
143
|
});
|
|
144
|
+
it("accepts plain Date with the date-fns adapter (Issue #64 regression)", async () => {
|
|
145
|
+
const adapter = await createDateFnsAdapter();
|
|
146
|
+
const dt = new Date(1990, 1, 1, 12, 10);
|
|
147
|
+
expect(() => getSaju(dt, {
|
|
148
|
+
adapter,
|
|
149
|
+
longitudeDeg: 126.9778,
|
|
150
|
+
gender: "male",
|
|
151
|
+
preset: STANDARD_PRESET,
|
|
152
|
+
})).not.toThrow();
|
|
153
|
+
const result = getSaju(dt, {
|
|
154
|
+
adapter,
|
|
155
|
+
longitudeDeg: 126.9778,
|
|
156
|
+
gender: "male",
|
|
157
|
+
preset: STANDARD_PRESET,
|
|
158
|
+
});
|
|
159
|
+
expect(result.pillars.year).toBeDefined();
|
|
160
|
+
expect(result.pillars.month).toBeDefined();
|
|
161
|
+
expect(result.pillars.day).toBeDefined();
|
|
162
|
+
expect(result.pillars.hour).toBeDefined();
|
|
163
|
+
expect(result.lunar.lunarYear).toBeGreaterThan(0);
|
|
164
|
+
});
|
|
143
165
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { DateAdapter } from "../adapters/date-adapter";
|
|
2
|
-
interface
|
|
2
|
+
export interface ZonedDateFnsDate {
|
|
3
3
|
date: Date;
|
|
4
4
|
timeZone: string;
|
|
5
5
|
}
|
|
6
|
+
export type DateFnsDate = Date | ZonedDateFnsDate;
|
|
6
7
|
export declare function createDateFnsAdapter(): Promise<DateAdapter<DateFnsDate>>;
|
|
7
|
-
export {};
|
|
8
8
|
//# sourceMappingURL=date-fns.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"date-fns.d.ts","sourceRoot":"","sources":["../../src/adapters/date-fns.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,
|
|
1
|
+
{"version":3,"file":"date-fns.d.ts","sourceRoot":"","sources":["../../src/adapters/date-fns.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,gBAAgB,CAAC;AAgClD,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CA4D9E"}
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
function isZonedDate(date) {
|
|
2
|
+
return (typeof date === "object" &&
|
|
3
|
+
date !== null &&
|
|
4
|
+
"date" in date &&
|
|
5
|
+
date.date instanceof Date &&
|
|
6
|
+
typeof date.timeZone === "string");
|
|
7
|
+
}
|
|
8
|
+
function getNativeDate(date) {
|
|
9
|
+
return isZonedDate(date) ? date.date : date;
|
|
10
|
+
}
|
|
11
|
+
function getSystemTimeZone() {
|
|
12
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
13
|
+
}
|
|
14
|
+
function getTimeZone(date) {
|
|
15
|
+
return isZonedDate(date) ? date.timeZone : getSystemTimeZone();
|
|
16
|
+
}
|
|
17
|
+
function cloneWithTimeZone(date, timeZone) {
|
|
18
|
+
return { date, timeZone };
|
|
19
|
+
}
|
|
20
|
+
function preserveInputShape(input, date) {
|
|
21
|
+
return isZonedDate(input) ? cloneWithTimeZone(date, input.timeZone) : date;
|
|
22
|
+
}
|
|
1
23
|
export async function createDateFnsAdapter() {
|
|
2
24
|
let addMinutes;
|
|
3
25
|
let addDays;
|
|
@@ -31,43 +53,22 @@ export async function createDateFnsAdapter() {
|
|
|
31
53
|
throw new Error("date-fns or date-fns-tz is not installed. Install with: npm install date-fns date-fns-tz");
|
|
32
54
|
}
|
|
33
55
|
return {
|
|
34
|
-
getYear: (dateFns) => getYear(dateFns
|
|
35
|
-
getMonth: (dateFns) => getMonth(dateFns
|
|
36
|
-
getDay: (dateFns) => getDate(dateFns
|
|
37
|
-
getHour: (dateFns) => getHours(dateFns
|
|
38
|
-
getMinute: (dateFns) => getMinutes(dateFns
|
|
39
|
-
getSecond: (dateFns) => getSeconds(dateFns
|
|
40
|
-
getZoneName: (dateFns) => dateFns
|
|
41
|
-
plusMinutes: (dateFns, minutes) => (
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
timeZone: dateFns.timeZone,
|
|
52
|
-
}),
|
|
53
|
-
toUTC: (dateFns) => ({
|
|
54
|
-
date: fromZonedTime(dateFns.date, dateFns.timeZone),
|
|
55
|
-
timeZone: "UTC",
|
|
56
|
-
}),
|
|
57
|
-
toISO: (dateFns) => formatISO(dateFns.date),
|
|
58
|
-
toMillis: (dateFns) => dateFns.date.getTime(),
|
|
59
|
-
fromMillis: (millis, zone) => ({
|
|
60
|
-
date: new Date(millis),
|
|
61
|
-
timeZone: zone,
|
|
62
|
-
}),
|
|
63
|
-
createUTC: (year, month, day, hour, minute, second) => ({
|
|
64
|
-
date: new Date(Date.UTC(year, month - 1, day, hour, minute, second)),
|
|
65
|
-
timeZone: "UTC",
|
|
66
|
-
}),
|
|
67
|
-
setZone: (dateFns, zoneName) => ({
|
|
68
|
-
date: toZonedTime(dateFns.date, zoneName),
|
|
69
|
-
timeZone: zoneName,
|
|
70
|
-
}),
|
|
71
|
-
isGreaterThanOrEqual: (date1, date2) => date1.date >= date2.date,
|
|
56
|
+
getYear: (dateFns) => getYear(getNativeDate(dateFns)),
|
|
57
|
+
getMonth: (dateFns) => getMonth(getNativeDate(dateFns)) + 1,
|
|
58
|
+
getDay: (dateFns) => getDate(getNativeDate(dateFns)),
|
|
59
|
+
getHour: (dateFns) => getHours(getNativeDate(dateFns)),
|
|
60
|
+
getMinute: (dateFns) => getMinutes(getNativeDate(dateFns)),
|
|
61
|
+
getSecond: (dateFns) => getSeconds(getNativeDate(dateFns)),
|
|
62
|
+
getZoneName: (dateFns) => getTimeZone(dateFns),
|
|
63
|
+
plusMinutes: (dateFns, minutes) => preserveInputShape(dateFns, addMinutes(getNativeDate(dateFns), minutes)),
|
|
64
|
+
plusDays: (dateFns, days) => preserveInputShape(dateFns, addDays(getNativeDate(dateFns), days)),
|
|
65
|
+
minusDays: (dateFns, days) => preserveInputShape(dateFns, subDays(getNativeDate(dateFns), days)),
|
|
66
|
+
toUTC: (dateFns) => cloneWithTimeZone(fromZonedTime(getNativeDate(dateFns), getTimeZone(dateFns)), "UTC"),
|
|
67
|
+
toISO: (dateFns) => formatISO(getNativeDate(dateFns)),
|
|
68
|
+
toMillis: (dateFns) => getNativeDate(dateFns).getTime(),
|
|
69
|
+
fromMillis: (millis, zone) => cloneWithTimeZone(new Date(millis), zone),
|
|
70
|
+
createUTC: (year, month, day, hour, minute, second) => cloneWithTimeZone(new Date(Date.UTC(year, month - 1, day, hour, minute, second)), "UTC"),
|
|
71
|
+
setZone: (dateFns, zoneName) => cloneWithTimeZone(toZonedTime(getNativeDate(dateFns), zoneName), zoneName),
|
|
72
|
+
isGreaterThanOrEqual: (date1, date2) => getNativeDate(date1) >= getNativeDate(date2),
|
|
72
73
|
};
|
|
73
74
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"yongshen.d.ts","sourceRoot":"","sources":["../../src/core/yongshen.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,OAAO,EACZ,KAAK,YAAY,EAIlB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE3F,MAAM,WAAW,mBAAoB,SAAQ,KAAK,CAAC,iBAAiB,CAAC;CAAG;AAUxE,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,iBAAiB,GAAG,mBAAmB,CAGlF;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,YAAY,GAAG,IAAI,CAAC;IAC/B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC,OAAO,EAAE;QAAE,UAAU,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACzE,cAAc,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,8BAA8B;IAC9B,kBAAkB,CAAC,EAAE;QACnB,OAAO,EAAE,YAAY,CAAC;QACtB,SAAS,EAAE,YAAY,GAAG,IAAI,CAAC;KAChC,CAAC;CACH;AAED,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"yongshen.d.ts","sourceRoot":"","sources":["../../src/core/yongshen.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,OAAO,EACZ,KAAK,YAAY,EAIlB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE3F,MAAM,WAAW,mBAAoB,SAAQ,KAAK,CAAC,iBAAiB,CAAC;CAAG;AAUxE,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,iBAAiB,GAAG,mBAAmB,CAGlF;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,YAAY,GAAG,IAAI,CAAC;IAC/B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC,OAAO,EAAE;QAAE,UAAU,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACzE,cAAc,EAAE,YAAY,GAAG,IAAI,CAAC;IACpC,8BAA8B;IAC9B,kBAAkB,CAAC,EAAE;QACnB,OAAO,EAAE,YAAY,CAAC;QACtB,SAAS,EAAE,YAAY,GAAG,IAAI,CAAC;KAChC,CAAC;CACH;AAED,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAwNrC,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,cAAc,CA2FhB;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,cAAc,GAAG;IACnE,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CA0BA"}
|
package/dist/core/yongshen.js
CHANGED
|
@@ -102,7 +102,7 @@ function getYokbuYongShen(dayMasterElement, level) {
|
|
|
102
102
|
const secondary = dayMasterElement;
|
|
103
103
|
return { primary, secondary };
|
|
104
104
|
}
|
|
105
|
-
function
|
|
105
|
+
function countElements(elements) {
|
|
106
106
|
const elementCounts = {
|
|
107
107
|
wood: 0,
|
|
108
108
|
fire: 0,
|
|
@@ -110,44 +110,56 @@ function hasSpecialFormation(dayMasterElement, level, allElements) {
|
|
|
110
110
|
metal: 0,
|
|
111
111
|
water: 0,
|
|
112
112
|
};
|
|
113
|
-
for (const elem of
|
|
113
|
+
for (const elem of elements) {
|
|
114
114
|
elementCounts[elem]++;
|
|
115
115
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
if (dominantElement && maxCount >= 3) {
|
|
127
|
-
// Determine specific 종격 type
|
|
128
|
-
const dmControls = CONTROLS[dayMasterElement];
|
|
129
|
-
const dmControlledBy = CONTROLLED_BY[dayMasterElement];
|
|
130
|
-
const dmGenerates = GENERATES[dayMasterElement];
|
|
131
|
-
let type = "종격";
|
|
132
|
-
if (dominantElement === dmControls) {
|
|
133
|
-
type = "종재격"; // Follow Wealth
|
|
134
|
-
}
|
|
135
|
-
else if (dominantElement === dmControlledBy) {
|
|
136
|
-
type = "종살격"; // Follow Killings
|
|
137
|
-
}
|
|
138
|
-
else if (dominantElement === dmGenerates) {
|
|
139
|
-
type = "종아격"; // Follow Children/Output
|
|
140
|
-
}
|
|
141
|
-
return { isSpecial: true, type, followElement: dominantElement };
|
|
116
|
+
return elementCounts;
|
|
117
|
+
}
|
|
118
|
+
function getDominantNonDayMasterElement(dayMasterElement, elementCounts) {
|
|
119
|
+
let dominantElement = null;
|
|
120
|
+
let maxCount = 0;
|
|
121
|
+
for (const [elem, count] of Object.entries(elementCounts)) {
|
|
122
|
+
if (count > maxCount && elem !== dayMasterElement) {
|
|
123
|
+
maxCount = count;
|
|
124
|
+
dominantElement = elem;
|
|
142
125
|
}
|
|
143
126
|
}
|
|
127
|
+
return { dominantElement, maxCount };
|
|
128
|
+
}
|
|
129
|
+
function getFollowFormationType(dayMasterElement, dominantElement) {
|
|
130
|
+
const followTypeByElement = {
|
|
131
|
+
[CONTROLS[dayMasterElement]]: "종재격",
|
|
132
|
+
[CONTROLLED_BY[dayMasterElement]]: "종살격",
|
|
133
|
+
[GENERATES[dayMasterElement]]: "종아격",
|
|
134
|
+
};
|
|
135
|
+
return followTypeByElement[dominantElement] ?? "종격";
|
|
136
|
+
}
|
|
137
|
+
function getExtremelyWeakSpecialFormation(dayMasterElement, elementCounts) {
|
|
138
|
+
const { dominantElement, maxCount } = getDominantNonDayMasterElement(dayMasterElement, elementCounts);
|
|
139
|
+
if (!dominantElement || maxCount < 3) {
|
|
140
|
+
return { isSpecial: false, type: null, followElement: null };
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
isSpecial: true,
|
|
144
|
+
type: getFollowFormationType(dayMasterElement, dominantElement),
|
|
145
|
+
followElement: dominantElement,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function getExtremelyStrongSpecialFormation(dayMasterElement, elementCounts) {
|
|
149
|
+
const controllerElement = CONTROLLED_BY[dayMasterElement];
|
|
150
|
+
const hasController = elementCounts[controllerElement] > 0;
|
|
151
|
+
if (hasController) {
|
|
152
|
+
return { isSpecial: false, type: null, followElement: null };
|
|
153
|
+
}
|
|
154
|
+
return { isSpecial: true, type: "종강격", followElement: dayMasterElement };
|
|
155
|
+
}
|
|
156
|
+
function hasSpecialFormation(dayMasterElement, level, allElements) {
|
|
157
|
+
const elementCounts = countElements(allElements);
|
|
158
|
+
if (level.key === "extremelyWeak") {
|
|
159
|
+
return getExtremelyWeakSpecialFormation(dayMasterElement, elementCounts);
|
|
160
|
+
}
|
|
144
161
|
if (level.key === "extremelyStrong") {
|
|
145
|
-
|
|
146
|
-
const controllerElement = CONTROLLED_BY[dayMasterElement];
|
|
147
|
-
const hasController = elementCounts[controllerElement] > 0;
|
|
148
|
-
if (!hasController) {
|
|
149
|
-
return { isSpecial: true, type: "종강격", followElement: dayMasterElement };
|
|
150
|
-
}
|
|
162
|
+
return getExtremelyStrongSpecialFormation(dayMasterElement, elementCounts);
|
|
151
163
|
}
|
|
152
164
|
return { isSpecial: false, type: null, followElement: null };
|
|
153
165
|
}
|