• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /********************************************************************
2  * COPYRIGHT:
3  * Copyright (c) 2003-2007, International Business Machines Corporation
4  * and others. All Rights Reserved.
5  ********************************************************************
6  * Calendar Case Test is a type of CalendarTest which compares the
7  * behavior of a calendar to a certain set of 'test cases', involving
8  * conversion between julian-day to fields and vice versa.
9  ********************************************************************/
10 
11 #include "calcasts.h"
12 
13 #if !UCONFIG_NO_FORMATTING
14 // ======= 'Main' ===========================
15 
16 #include "hebrwcal.h" // for Eras
17 #include "indiancal.h"
18 #include "unicode/datefmt.h"
19 
20 #define CASE(id,test) case id: name = #test; if (exec) { logln(#test "---"); logln((UnicodeString)""); test(); } break
21 
22 
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)23 void CalendarCaseTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ )
24 {
25     if (exec) logln("TestSuite CalendarCaseTest");
26     switch (index) {
27     CASE(0,IslamicCivil);
28     CASE(1,Hebrew);
29     CASE(2,Indian);
30     default: name = ""; break;
31     }
32 }
33 
34 #undef CASE
35 
36 // ======= Utility functions =================
37 
doTestCases(const TestCase * cases,Calendar * cal)38 void CalendarCaseTest::doTestCases(const TestCase *cases, Calendar *cal) {
39   static const int32_t  ONE_SECOND = 1000;
40   static const int32_t  ONE_MINUTE = 60*ONE_SECOND;
41   static const int32_t  ONE_HOUR   = 60*ONE_MINUTE;
42   static const double ONE_DAY    = 24*ONE_HOUR;
43   static const double JULIAN_EPOCH = -210866760000000.;   // 1/1/4713 BC 12:00
44   int32_t i;
45   UErrorCode status = U_ZERO_ERROR;
46   cal->adoptTimeZone(TimeZone::getGMT()->clone());
47   for(i=0;cases[i].era>=0;i++) {
48     UDate t = (JULIAN_EPOCH+(ONE_DAY*cases[i].julian));
49 
50     logln("Test case %d:  julianday%f -> date %f\n", i, cases[i].julian, t);
51 
52     // Millis -> fields
53     cal->setTime(t, status);
54 
55     logln(calToStr(*cal));
56 
57     checkField(cal, UCAL_ERA, cases[i].era, status);
58     checkField(cal, UCAL_YEAR, cases[i].year,status);
59     checkField(cal, UCAL_MONTH, cases[i].month - 1,status);
60     checkField(cal, UCAL_DATE, cases[i].day,status);
61     checkField(cal, UCAL_DAY_OF_WEEK, cases[i].dayOfWeek,status);
62     checkField(cal, UCAL_HOUR, cases[i].hour,status);
63     checkField(cal, UCAL_MINUTE, cases[i].min,status);
64     checkField(cal, UCAL_SECOND, cases[i].sec,status);
65 
66     // Fields -> millis
67     cal->clear();
68 
69     cal->set(UCAL_ERA, cases[i].era);
70     cal->set(UCAL_YEAR, cases[i].year);
71     cal->set(UCAL_MONTH, cases[i].month - 1);
72     cal->set(UCAL_DATE, cases[i].day);
73     cal->set(UCAL_DAY_OF_WEEK, cases[i].dayOfWeek);
74     cal->set(UCAL_HOUR, cases[i].hour);
75     cal->set(UCAL_MINUTE, cases[i].min);
76     cal->set(UCAL_SECOND, cases[i].sec);
77 
78     UDate t2 = cal->getTime(status);
79 
80     if(t != t2) {
81       errln("Field->millis: Expected %.0f but got %.0f\n", t, t2);
82       logln(calToStr(*cal));
83     }
84   }
85 }
86 
checkField(Calendar * cal,UCalendarDateFields field,int32_t value,UErrorCode & status)87 UBool CalendarCaseTest::checkField(Calendar *cal, UCalendarDateFields field, int32_t value, UErrorCode &status)
88 {
89   if(U_FAILURE(status)) return FALSE;
90   int32_t res = cal->get(field, status);
91   if(U_FAILURE(status)) {
92     errln((UnicodeString)"Checking field " + fieldName(field) + " and got " + u_errorName(status));
93     return FALSE;
94   }
95   if(res != value) {
96     errln((UnicodeString)"FAIL: Checking field " + fieldName(field) + " expected " + value + " and got " + res + UnicodeString("\n"));
97     return FALSE;
98   } else {
99     logln((UnicodeString)"Checking field " + fieldName(field) + " == " + value + UnicodeString("\n"));
100   }
101   return TRUE;
102 }
103 
104 // =========== Test Cases =====================
105 enum { SUN=UCAL_SUNDAY,
106        MON=UCAL_MONDAY,
107        TUE=UCAL_TUESDAY,
108        WED=UCAL_WEDNESDAY,
109        THU=UCAL_THURSDAY,
110        FRI=UCAL_FRIDAY,
111        SAT=UCAL_SATURDAY};
112 
IslamicCivil()113 void CalendarCaseTest::IslamicCivil()
114 {
115     static const TestCase tests[] = {
116         //
117         // Most of these test cases were taken from the back of
118         // "Calendrical Calculations", with some extras added to help
119         // debug a few of the problems that cropped up in development.
120         //
121         // The months in this table are 1-based rather than 0-based,
122         // because it's easier to edit that way.
123         //                       Islamic
124         //          Julian Day  Era  Year  Month Day  WkDay Hour Min Sec
125         { 1507231.5,  0, -1245,   12,   9,  SUN,   0,  0,  0},
126         { 1660037.5,  0,  -813,    2,  23,  WED,   0,  0,  0},
127         { 1746893.5,  0,  -568,    4,   1,  WED,   0,  0,  0},
128         { 1770641.5,  0,  -501,    4,   6,  SUN,   0,  0,  0},
129         { 1892731.5,  0,  -157,   10,  17,  WED,   0,  0,  0},
130         { 1931579.5,  0,   -47,    6,   3,  MON,   0,  0,  0},
131         { 1974851.5,  0,    75,    7,  13,  SAT,   0,  0,  0},
132         { 2091164.5,  0,   403,   10,   5,  SUN,   0,  0,  0},
133         { 2121509.5,  0,   489,    5,  22,  SUN,   0,  0,  0},
134         { 2155779.5,  0,   586,    2,   7,  FRI,   0,  0,  0},
135         { 2174029.5,  0,   637,    8,   7,  SAT,   0,  0,  0},
136         { 2191584.5,  0,   687,    2,  20,  FRI,   0,  0,  0},
137         { 2195261.5,  0,   697,    7,   7,  SUN,   0,  0,  0},
138         { 2229274.5,  0,   793,    7,   1,  SUN,   0,  0,  0},
139         { 2245580.5,  0,   839,    7,   6,  WED,   0,  0,  0},
140         { 2266100.5,  0,   897,    6,   1,  SAT,   0,  0,  0},
141         { 2288542.5,  0,   960,    9,  30,  SAT,   0,  0,  0},
142         { 2290901.5,  0,   967,    5,  27,  SAT,   0,  0,  0},
143         { 2323140.5,  0,  1058,    5,  18,  WED,   0,  0,  0},
144         { 2334848.5,  0,  1091,    6,   2,  SUN,   0,  0,  0},
145         { 2348020.5,  0,  1128,    8,   4,  FRI,   0,  0,  0},
146         { 2366978.5,  0,  1182,    2,   3,  SUN,   0,  0,  0},
147         { 2385648.5,  0,  1234,   10,  10,  MON,   0,  0,  0},
148         { 2392825.5,  0,  1255,    1,  11,  WED,   0,  0,  0},
149         { 2416223.5,  0,  1321,    1,  21,  SUN,   0,  0,  0},
150         { 2425848.5,  0,  1348,    3,  19,  SUN,   0,  0,  0},
151         { 2430266.5,  0,  1360,    9,   8,  MON,   0,  0,  0},
152         { 2430833.5,  0,  1362,    4,  13,  MON,   0,  0,  0},
153         { 2431004.5,  0,  1362,   10,   7,  THU,   0,  0,  0},
154         { 2448698.5,  0,  1412,    9,  13,  TUE,   0,  0,  0},
155         { 2450138.5,  0,  1416,   10,   5,  SUN,   0,  0,  0},
156         { 2465737.5,  0,  1460,   10,  12,  WED,   0,  0,  0},
157         { 2486076.5,  0,  1518,    3,   5,  SUN,   0,  0,  0},
158         { -1,-1,-1,-1,-1,-1,-1,-1,-1 }
159     };
160 
161     UErrorCode status = U_ZERO_ERROR;
162     Calendar *c = Calendar::createInstance("ar@calendar=islamic-civil", status);
163     c->setLenient(TRUE);
164     doTestCases(tests, c);
165 
166     static const UChar expectedUChars[] = {
167         0x0627, 0x0644, 0x062e, 0x0645, 0x064a, 0x0633, 0x002c, 0x0020, 0x0662, 0x0662,
168         0x0020, 0x0634, 0x0648, 0x0627, 0x0644, 0x002c, 0x0020, 0x0661, 0x0663, 0x0668,
169         0x0669, 0
170     };
171     UnicodeString result;
172     DateFormat *fmt = DateFormat::createDateInstance(DateFormat::kFull, Locale("ar_JO@calendar=islamic-civil"));
173     if (fmt == NULL) {
174         dataerrln("Error calling DateFormat::createDateInstance");
175         delete c;
176         delete fmt;
177         return;
178     }
179 
180     fmt->setTimeZone(*TimeZone::getGMT());
181     fmt->format((UDate)2486076.5, result);
182     if (result != expectedUChars) {
183         errln((UnicodeString)"FAIL: DateFormatting failed. Got " + result + " and expected " + UnicodeString(expectedUChars) + UnicodeString("\n"));
184         errln("Maybe the resource aliasing isn't working");
185     }
186     delete fmt;
187     delete c;
188 }
189 
Hebrew()190 void CalendarCaseTest::Hebrew() {
191     static const int32_t TISHRI  = HebrewCalendar::TISHRI;
192     //static const int32_t HESHVAN = HebrewCalendar::HESHVAN;
193     //static const int32_t KISLEV  = HebrewCalendar::KISLEV;
194     //static const int32_t TEVET   = HebrewCalendar::TEVET;
195     //static const int32_t SHEVAT  = HebrewCalendar::SHEVAT;
196     //static const int32_t ADAR_1  = HebrewCalendar::ADAR_1;
197     //static const int32_t ADAR    = HebrewCalendar::ADAR;
198     //static const int32_t NISAN   = HebrewCalendar::NISAN;
199     //static const int32_t IYAR    = HebrewCalendar::IYAR;
200     //static const int32_t SIVAN   = HebrewCalendar::SIVAN;
201     //static const int32_t TAMUZ   = HebrewCalendar::TAMUZ;
202     static const int32_t AV      = HebrewCalendar::AV;
203     static const int32_t ELUL    = HebrewCalendar::ELUL;
204 
205     static const TestCase tests[] = {
206         //
207         // Most of these test cases were taken from the back of
208         // "Calendrical Calculations", with some extras added to help
209         // debug a few of the problems that cropped up in development.
210         //
211         // The months in this table are 1-based rather than 0-based,
212         // because it's easier to edit that way.
213         //
214         //         Julian Day  Era  Year  Month Day  WkDay Hour Min Sec
215         {1507231.5,  0,  3174,   12,  10,  SUN,   0,  0,  0},
216         {1660037.5,  0,  3593,    3,  25,  WED,   0,  0,  0},
217         {1746893.5,  0,  3831,    1,   3,  WED,   0,  0,  0},
218         {1770641.5,  0,  3896,    1,   9,  SUN,   0,  0,  0},
219         {1892731.5,  0,  4230,    4,  18,  WED,   0,  0,  0},
220         {1931579.5,  0,  4336,   10,   4,  MON,   0,  0,  0},
221         {1974851.5,  0,  4455,    2,  13,  SAT,   0,  0,  0},
222         {2091164.5,  0,  4773,    9,   6,  SUN,   0,  0,  0},
223         {2121509.5,  0,  4856,    9,  23,  SUN,   0,  0,  0},
224         {2155779.5,  0,  4950,    8,   7,  FRI,   0,  0,  0},
225         {2174029.5,  0,  5000,    7,   8,  SAT,   0,  0,  0},
226         {2191584.5,  0,  5048,    8,  21,  FRI,   0,  0,  0},
227         {2195261.5,  0,  5058,    9,   7,  SUN,   0,  0,  0},
228         {2229274.5,  0,  5151,   11,   1,  SUN,   0,  0,  0},
229         {2245580.5,  0,  5196,    5,   7,  WED,   0,  0,  0},
230         {2266100.5,  0,  5252,    8,   3,  SAT,   0,  0,  0},
231         {2288542.5,  0,  5314,    1,   1,  SAT,   0,  0,  0},
232         {2290901.5,  0,  5320,    6,  27,  SAT,   0,  0,  0},
233         {2323140.5,  0,  5408,   10,  20,  WED,   0,  0,  0},
234         {2334551.5,  0,  5440,    1,   1,  THU,   0,  0,  0},
235         {2334581.5,  0,  5440,    2,   1,  SAT,   0,  0,  0},
236         {2334610.5,  0,  5440,    3,   1,  SUN,   0,  0,  0},
237         {2334639.5,  0,  5440,    4,   1,  MON,   0,  0,  0},
238         {2334668.5,  0,  5440,    5,   1,  TUE,   0,  0,  0},
239         {2334698.5,  0,  5440,    6,   1,  THU,   0,  0,  0},
240         {2334728.5,  0,  5440,    7,   1,  SAT,   0,  0,  0},
241         {2334757.5,  0,  5440,    8,   1,  SUN,   0,  0,  0},
242         {2334787.5,  0,  5440,    9,   1,  TUE,   0,  0,  0},
243         {2334816.5,  0,  5440,   10,   1,  WED,   0,  0,  0},
244         {2334846.5,  0,  5440,   11,   1,  FRI,   0,  0,  0},
245         {2334848.5,  0,  5440,   11,   3,  SUN,   0,  0,  0},
246         {2334934.5,  0,  5441,    1,   1,  TUE,   0,  0,  0},
247         {2348020.5,  0,  5476,   12,   5,  FRI,   0,  0,  0},
248         {2366978.5,  0,  5528,   11,   4,  SUN,   0,  0,  0},
249         {2385648.5,  0,  5579,   12,  11,  MON,   0,  0,  0},
250         {2392825.5,  0,  5599,    8,  12,  WED,   0,  0,  0},
251         {2416223.5,  0,  5663,    8,  22,  SUN,   0,  0,  0},
252         {2425848.5,  0,  5689,   12,  19,  SUN,   0,  0,  0},
253         {2430266.5,  0,  5702,    1,   8,  MON,   0,  0,  0},
254         {2430833.5,  0,  5703,    8,  14,  MON,   0,  0,  0},
255         {2431004.5,  0,  5704,    1,   8,  THU,   0,  0,  0},
256         {2448698.5,  0,  5752,    7,  12,  TUE,   0,  0,  0},
257         {2450138.5,  0,  5756,    7,   5,  SUN,   0,  0,  0},
258         {2465737.5,  0,  5799,    2,  12,  WED,   0,  0,  0},
259         {2486076.5,  0,  5854,   12,   5,  SUN,   0,  0,  0},
260 
261         // Test cases taken from a table of 14 "year types" in the Help file
262         // of the application "Hebrew Calendar"
263         {2456187.5,  0,  5773,    1,   1,  MON,   0,  0,  0},
264         {2459111.5,  0,  5781,    1,   1,  SAT,   0,  0,  0},
265         {2453647.5,  0,  5766,    1,   1,  TUE,   0,  0,  0},
266         {2462035.5,  0,  5789,    1,   1,  THU,   0,  0,  0},
267         {2458756.5,  0,  5780,    1,   1,  MON,   0,  0,  0},
268         {2460586.5,  0,  5785,    1,   1,  THU,   0,  0,  0},
269         {2463864.5,  0,  5794,    1,   1,  SAT,   0,  0,  0},
270         {2463481.5,  0,  5793,    1,   1,  MON,   0,  0,  0},
271         {2470421.5,  0,  5812,    1,   1,  THU,   0,  0,  0},
272         {2460203.5,  0,  5784,    1,   1,  SAT,   0,  0,  0},
273         {2459464.5,  0,  5782,    1,   1,  TUE,   0,  0,  0},
274         {2467142.5,  0,  5803,    1,   1,  MON,   0,  0,  0},
275         {2455448.5,  0,  5771,    1,   1,  THU,   0,  0,  0},
276         // Test cases for JB#2327
277         // http://www.fourmilab.com/documents/calendar/
278         // http://www.calendarhome.com/converter/
279         //      2452465.5, 2002, JULY, 10, 5762, AV, 1,
280         //      2452494.5, 2002, AUGUST, 8, 5762, AV, 30,
281         //      2452495.5, 2002, AUGUST, 9, 5762, ELUL, 1,
282         //      2452523.5, 2002, SEPTEMBER, 6, 5762, ELUL, 29,
283         //      2452524.5, 2002, SEPTEMBER, 7, 5763, TISHRI, 1,
284         //         Julian Day  Era  Year  Month Day  WkDay Hour Min Sec
285         {2452465.5,  0,  5762,    AV+1,  1,  WED,   0,  0,  0},
286         {2452494.5,  0,  5762,    AV+1, 30,  THU,   0,  0,  0},
287         {2452495.5,  0,  5762,  ELUL+1,  1,  FRI,   0,  0,  0},
288         {2452523.5,  0,  5762,  ELUL+1, 29,  FRI,   0,  0,  0},
289         {2452524.5,  0,  5763, TISHRI+1,  1,  SAT,   0,  0,  0},
290         { -1,-1,-1,-1,-1,-1,-1,-1,-1 }
291     };
292 
293     UErrorCode status = U_ZERO_ERROR;
294     Calendar *c = Calendar::createInstance("he_HE@calendar=hebrew", status);
295     c->setLenient(TRUE);
296     doTestCases(tests, c);
297 
298 
299     // Additional test cases for bugs found during development
300     //           G.YY/MM/DD  Era  Year  Month Day  WkDay Hour Min Sec
301     //{1013, 9, 8, 0,  4774,    1,   1,  TUE,   0,  0,  0},
302     //{1239, 9, 1, 0,  5000,    1,   1,  THU,   0,  0,  0},
303     //{1240, 9,18, 0,  5001,    1,   1,  TUE,   0,  0,  0},
304 
305 
306     delete c;
307 }
308 
Indian()309 void CalendarCaseTest::Indian() {
310     // Months in indian calendar are 0-based. Here taking 1-based names:
311     static const int32_t CHAITRA    = IndianCalendar::CHAITRA + 1;
312     static const int32_t VAISAKHA   = IndianCalendar::VAISAKHA + 1;
313     static const int32_t JYAISTHA   = IndianCalendar::JYAISTHA + 1;
314     static const int32_t ASADHA     = IndianCalendar::ASADHA + 1;
315     static const int32_t SRAVANA    = IndianCalendar::SRAVANA + 1 ;
316     static const int32_t BHADRA     = IndianCalendar::BHADRA + 1 ;
317     static const int32_t ASVINA     = IndianCalendar::ASVINA  + 1 ;
318     static const int32_t KARTIKA    = IndianCalendar::KARTIKA + 1 ;
319     static const int32_t AGRAHAYANA = IndianCalendar::AGRAHAYANA + 1 ;
320     static const int32_t PAUSA      = IndianCalendar::PAUSA + 1 ;
321     static const int32_t MAGHA      = IndianCalendar::MAGHA + 1 ;
322     static const int32_t PHALGUNA   = IndianCalendar::PHALGUNA + 1 ;
323 
324 
325     static const TestCase tests[] = {
326     // Test dates generated from:
327     // http://www.fourmilab.ch/documents/calendar/
328 
329     // A huge list of test cases to make sure that computeTime and computeFields
330     // work properly for a wide range of data in the Indian civil calendar.
331     //
332     // Julian Day   Era Year     Month         Day  WkDay Hour Min Sec
333        {1770641.5,  0,    57,    ASVINA,       10,  SUN,   0,  0,  0},
334        {1892731.5,  0,   391,    PAUSA,        18,  WED,   0,  0,  0},
335        {1931579.5,  0,   498,    VAISAKHA,     30,  MON,   0,  0,  0},
336        {1974851.5,  0,   616,    KARTIKA,      19,  SAT,   0,  0,  0},
337        {2091164.5,  0,   935,    VAISAKHA,      5,  SUN,   0,  0,  0},
338        {2121509.5,  0,  1018,    JYAISTHA,      3,  SUN,   0,  0,  0},
339        {2155779.5,  0,  1112,    CHAITRA,       2,  FRI,   0,  0,  0},
340        {2174029.5,  0,  1161,    PHALGUNA,     20,  SAT,   0,  0,  0},
341        {2191584.5,  0,  1210,    CHAITRA,      13,  FRI,   0,  0,  0},
342        {2195261.5,  0,  1220,    VAISAKHA,      7,  SUN,   0,  0,  0},
343        {2229274.5,  0,  1313,    JYAISTHA,     22,  SUN,   0,  0,  0},
344        {2245580.5,  0,  1357,    MAGHA,        14,  WED,   0,  0,  0},
345        {2266100.5,  0,  1414,    CHAITRA,      20,  SAT,   0,  0,  0},
346        {2288542.5,  0,  1475,    BHADRA,       28,  SAT,   0,  0,  0},
347        {2290901.5,  0,  1481,    PHALGUNA,     15,  SAT,   0,  0,  0},
348        {2323140.5,  0,  1570,    JYAISTHA,     20,  WED,   0,  0,  0},
349        {2334551.5,  0,  1601,    BHADRA,       16,  THU,   0,  0,  0},
350        {2334581.5,  0,  1601,    ASVINA,       15,  SAT,   0,  0,  0},
351        {2334610.5,  0,  1601,    KARTIKA,      14,  SUN,   0,  0,  0},
352        {2334639.5,  0,  1601,    AGRAHAYANA,   13,  MON,   0,  0,  0},
353        {2334668.5,  0,  1601,    PAUSA,        12,  TUE,   0,  0,  0},
354        {2334698.5,  0,  1601,    MAGHA,        12,  THU,   0,  0,  0},
355        {2334728.5,  0,  1601,    PHALGUNA,     12,  SAT,   0,  0,  0},
356        {2334757.5,  0,  1602,    CHAITRA,      11,  SUN,   0,  0,  0},
357        {2334787.5,  0,  1602,    VAISAKHA,     10,  TUE,   0,  0,  0},
358        {2334816.5,  0,  1602,    JYAISTHA,      8,  WED,   0,  0,  0},
359        {2334846.5,  0,  1602,    ASADHA,        7,  FRI,   0,  0,  0},
360        {2334848.5,  0,  1602,    ASADHA,        9,  SUN,   0,  0,  0},
361        {2348020.5,  0,  1638,    SRAVANA,       2,  FRI,   0,  0,  0},
362        {2334934.5,  0,  1602,    ASVINA,        2,  TUE,   0,  0,  0},
363        {2366978.5,  0,  1690,    JYAISTHA,     29,  SUN,   0,  0,  0},
364        {2385648.5,  0,  1741,    SRAVANA,      11,  MON,   0,  0,  0},
365        {2392825.5,  0,  1761,    CHAITRA,       6,  WED,   0,  0,  0},
366        {2416223.5,  0,  1825,    CHAITRA,      29,  SUN,   0,  0,  0},
367        {2425848.5,  0,  1851,    BHADRA,        3,  SUN,   0,  0,  0},
368        {2430266.5,  0,  1863,    ASVINA,        7,  MON,   0,  0,  0},
369        {2430833.5,  0,  1865,    CHAITRA,      29,  MON,   0,  0,  0},
370        {2431004.5,  0,  1865,    ASVINA,       15,  THU,   0,  0,  0},
371        {2448698.5,  0,  1913,    PHALGUNA,     27,  TUE,   0,  0,  0},
372        {2450138.5,  0,  1917,    PHALGUNA,      6,  SUN,   0,  0,  0},
373        {2465737.5,  0,  1960,    KARTIKA,      19,  WED,   0,  0,  0},
374        {2486076.5,  0,  2016,    ASADHA,       27,  SUN,   0,  0,  0},
375         { -1,-1,-1,-1,-1,-1,-1,-1,-1 }
376     };
377 
378     UErrorCode status = U_ZERO_ERROR;
379     Calendar *c = Calendar::createInstance("hi_IN@calendar=indian", status);
380     c->setLenient(TRUE);
381     doTestCases(tests, c);
382 
383     delete c;
384 }
385 
386 #endif
387