OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [gnu/] [java/] [util/] [ZoneInfo.java] - Blame information for rev 769

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 769 jeremybenn
/* gnu.java.util.ZoneInfo
2
   Copyright (C) 2007 Free Software Foundation, Inc.
3
 
4
This file is part of GNU Classpath.
5
 
6
GNU Classpath is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
9
any later version.
10
 
11
GNU Classpath is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
General Public License for more details.
15
 
16
You should have received a copy of the GNU General Public License
17
along with GNU Classpath; see the file COPYING.  If not, write to the
18
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
02110-1301 USA.
20
 
21
Linking this library statically or dynamically with other modules is
22
making a combined work based on this library.  Thus, the terms and
23
conditions of the GNU General Public License cover the whole
24
combination.
25
 
26
As a special exception, the copyright holders of this library give you
27
permission to link this library with independent modules to produce an
28
executable, regardless of the license terms of these independent
29
modules, and to copy and distribute the resulting executable under
30
terms of your choice, provided that you also meet, for each linked
31
independent module, the terms and conditions of the license of that
32
module.  An independent module is a module which is not derived from
33
or based on this library.  If you modify this library, you may extend
34
this exception to your version of the library, but you are not
35
obligated to do so.  If you do not wish to do so, delete this
36
exception statement from your version. */
37
 
38
 
39
package gnu.java.util;
40
 
41
import java.io.BufferedInputStream;
42
import java.io.DataInputStream;
43
import java.io.EOFException;
44
import java.io.FileInputStream;
45
import java.io.InputStream;
46
import java.io.IOException;
47
import java.util.Calendar;
48
import java.util.Date;
49
import java.util.GregorianCalendar;
50
import java.util.SimpleTimeZone;
51
import java.util.TimeZone;
52
 
53
/**
54
 * This class represents more advanced variant of java.util.SimpleTimeZone.
55
 * It can handle zic(8) compiled transition dates plus uses a SimpleTimeZone
56
 * for years beyond last precomputed transition.  Before first precomputed
57
 * transition it assumes no daylight saving was in effect.
58
 * Timezones that never used daylight saving time should use just
59
 * SimpleTimeZone instead of this class.
60
 *
61
 * This object is tightly bound to the Gregorian calendar.  It assumes
62
 * a regular seven days week, and the month lengths are that of the
63
 * Gregorian Calendar.
64
 *
65
 * @see Calendar
66
 * @see GregorianCalendar
67
 * @see SimpleTimeZone
68
 * @author Jakub Jelinek
69
 */
70
public class ZoneInfo extends TimeZone
71
{
72
  private static final int SECS_SHIFT = 22;
73
  private static final long OFFSET_MASK = (1 << 21) - 1;
74
  private static final int OFFSET_SHIFT = 64 - 21;
75
  private static final long IS_DST = 1 << 21;
76
 
77
  /**
78
   * The raw time zone offset in milliseconds to GMT, ignoring
79
   * daylight savings.
80
   * @serial
81
   */
82
  private int rawOffset;
83
 
84
  /**
85
   * Cached DST savings for the last transition rule.
86
   */
87
  private int dstSavings;
88
 
89
  /**
90
   * Cached flag whether last transition rule uses DST saving.
91
   */
92
  private boolean useDaylight;
93
 
94
  /**
95
   * Array of encoded transitions.
96
   * Transition time in UTC seconds since epoch is in the most
97
   * significant 64 - SECS_SHIFT bits, then one bit flag
98
   * whether DST is active and the least significant bits
99
   * containing offset relative to rawOffset.  Both the DST
100
   * flag and relative offset apply to time before the transition
101
   * and after or equal to previous transition if any.
102
   * The array must be sorted.
103
   */
104
  private long[] transitions;
105
 
106
  /**
107
   * SimpleTimeZone rule which applies on or after the latest
108
   * transition.  If the DST changes are not expresible as a
109
   * SimpleTimeZone rule, then the rule should just contain
110
   * the standard time and no DST time.
111
   */
112
  private SimpleTimeZone lastRule;
113
 
114
  /**
115
   * Cached GMT SimpleTimeZone object for internal use in
116
   * getOffset method.
117
   */
118
  private static SimpleTimeZone gmtZone = null;
119
 
120
  static final long serialVersionUID = -3740626706860383657L;
121
 
122
  /**
123
   * Create a <code>ZoneInfo</code> with the given time offset
124
   * from GMT and with daylight savings.
125
   *
126
   * @param rawOffset The time offset from GMT in milliseconds.
127
   * @param id  The identifier of this time zone.
128
   * @param transitions  Array of transition times in UTC seconds since
129
   * Epoch in topmost 42 bits, below that 1 boolean bit whether the time
130
   * before that transition used daylight saving and in bottommost 21
131
   * bits relative daylight saving offset against rawOffset in seconds
132
   * that applies before this transition.
133
   * @param endRule SimpleTimeZone class describing the daylight saving
134
   * rules after the last transition.
135
   */
136
  public ZoneInfo(int rawOffset, String id, long[] transitions,
137
                  SimpleTimeZone lastRule)
138
  {
139
    if (transitions == null || transitions.length < 1)
140
      throw new IllegalArgumentException("transitions must not be null");
141
    if (lastRule == null)
142
      throw new IllegalArgumentException("lastRule must not be null");
143
    this.rawOffset = rawOffset;
144
    this.transitions = transitions;
145
    this.lastRule = lastRule;
146
    setID(id);
147
    computeDSTSavings();
148
  }
149
 
150
  /**
151
   * Gets the time zone offset, for current date, modified in case of
152
   * daylight savings.  This is the offset to add to UTC to get the local
153
   * time.
154
   *
155
   * The day must be a positive number and dayOfWeek must be a positive value
156
   * from Calendar.  dayOfWeek is redundant, but must match the other values
157
   * or an inaccurate result may be returned.
158
   *
159
   * @param era the era of the given date
160
   * @param year the year of the given date
161
   * @param month the month of the given date, 0 for January.
162
   * @param day the day of month
163
   * @param dayOfWeek the day of week; this must match the other fields.
164
   * @param millis the millis in the day (in local standard time)
165
   * @return the time zone offset in milliseconds.
166
   * @throws IllegalArgumentException if arguments are incorrect.
167
   */
168
  public int getOffset(int era, int year, int month, int day, int dayOfWeek,
169
                       int millis)
170
  {
171
    if (gmtZone == null)
172
      gmtZone = new SimpleTimeZone(0, "GMT");
173
 
174
    if (dayOfWeek < Calendar.SUNDAY || dayOfWeek > Calendar.SATURDAY)
175
      throw new IllegalArgumentException("dayOfWeek out of range");
176
    if (month < Calendar.JANUARY || month > Calendar.DECEMBER)
177
      throw new IllegalArgumentException("month out of range:" + month);
178
 
179
    if (era != GregorianCalendar.AD)
180
      return (int) (((transitions[0] << OFFSET_SHIFT) >> OFFSET_SHIFT) * 1000);
181
 
182
    GregorianCalendar cal = new GregorianCalendar((TimeZone) gmtZone);
183
    cal.set(year, month, day, 0, 0, 0);
184
    if (cal.get(Calendar.DAY_OF_MONTH) != day)
185
      throw new IllegalArgumentException("day out of range");
186
 
187
    return getOffset(cal.getTimeInMillis() - rawOffset + millis);
188
  }
189
 
190
  private long findTransition(long secs)
191
  {
192
    if (secs < (transitions[0] >> SECS_SHIFT))
193
      return transitions[0];
194
 
195
    if (secs >= (transitions[transitions.length-1] >> SECS_SHIFT))
196
      return Long.MAX_VALUE;
197
 
198
    long val = (secs + 1) << SECS_SHIFT;
199
    int lo = 1;
200
    int hi = transitions.length;
201
    int mid = 1;
202
    while (lo < hi)
203
      {
204
        mid = (lo + hi) / 2;
205
        // secs < (transitions[mid-1] >> SECS_SHIFT)
206
        if (val <= transitions[mid-1])
207
          hi = mid;
208
        // secs >= (transitions[mid] >> SECS_SHIFT)
209
        else if (val > transitions[mid])
210
          lo = mid + 1;
211
        else
212
          break;
213
      }
214
    return transitions[mid];
215
  }
216
 
217
  /**
218
   * Get the time zone offset for the specified date, modified in case of
219
   * daylight savings.  This is the offset to add to UTC to get the local
220
   * time.
221
   * @param date the date represented in millisecends
222
   * since January 1, 1970 00:00:00 GMT.
223
   */
224
  public int getOffset(long date)
225
  {
226
    long d = (date >= 0 ? date / 1000 : (date + 1) / 1000 - 1);
227
    long transition = findTransition(d);
228
 
229
    // For times on or after last transition use lastRule.
230
    if (transition == Long.MAX_VALUE)
231
      return lastRule.getOffset(date);
232
 
233
    return (int) (((transition << OFFSET_SHIFT) >> OFFSET_SHIFT) * 1000);
234
  }
235
 
236
  /**
237
   * Returns the time zone offset to GMT in milliseconds, ignoring
238
   * day light savings.
239
   * @return the time zone offset.
240
   */
241
  public int getRawOffset()
242
  {
243
    return rawOffset;
244
  }
245
 
246
  /**
247
   * Sets the standard time zone offset to GMT.
248
   * @param rawOffset The time offset from GMT in milliseconds.
249
   */
250
  public void setRawOffset(int rawOffset)
251
  {
252
    this.rawOffset = rawOffset;
253
    lastRule.setRawOffset(rawOffset);
254
  }
255
 
256
  private void computeDSTSavings()
257
  {
258
    if (lastRule.useDaylightTime())
259
      {
260
        dstSavings = lastRule.getDSTSavings();
261
        useDaylight = true;
262
      }
263
    else
264
      {
265
        dstSavings = 0;
266
        useDaylight = false;
267
        // lastRule might say no DST is in effect simply because
268
        // the DST rules are too complex for SimpleTimeZone, say
269
        // for Asia/Jerusalem.
270
        // Look at the last DST offset if it is newer than current time.
271
        long currentSecs = System.currentTimeMillis() / 1000;
272
        int i;
273
        for (i = transitions.length - 1;
274
             i >= 0 && currentSecs < (transitions[i] >> SECS_SHIFT);
275
             i--)
276
          if ((transitions[i] & IS_DST) != 0)
277
            {
278
              dstSavings = (int) (((transitions[i] << OFFSET_SHIFT)
279
                                   >> OFFSET_SHIFT) * 1000)
280
                           - rawOffset;
281
              useDaylight = true;
282
              break;
283
            }
284
      }
285
  }
286
 
287
  /**
288
   * Gets the daylight savings offset.  This is a positive offset in
289
   * milliseconds with respect to standard time.  Typically this
290
   * is one hour, but for some time zones this may be half an our.
291
   * @return the daylight savings offset in milliseconds.
292
   */
293
  public int getDSTSavings()
294
  {
295
    return dstSavings;
296
  }
297
 
298
  /**
299
   * Returns if this time zone uses daylight savings time.
300
   * @return true, if we use daylight savings time, false otherwise.
301
   */
302
  public boolean useDaylightTime()
303
  {
304
    return useDaylight;
305
  }
306
 
307
  /**
308
   * Determines if the given date is in daylight savings time.
309
   * @return true, if it is in daylight savings time, false otherwise.
310
   */
311
  public boolean inDaylightTime(Date date)
312
  {
313
    long d = date.getTime();
314
    d = (d >= 0 ? d / 1000 : (d + 1) / 1000 - 1);
315
    long transition = findTransition(d);
316
 
317
    // For times on or after last transition use lastRule.
318
    if (transition == Long.MAX_VALUE)
319
      return lastRule.inDaylightTime(date);
320
 
321
    return (transition & IS_DST) != 0;
322
  }
323
 
324
  /**
325
   * Generates the hashCode for the SimpleDateFormat object.  It is
326
   * the rawOffset, possibly, if useDaylightSavings is true, xored
327
   * with startYear, startMonth, startDayOfWeekInMonth, ..., endTime.
328
   */
329
  public synchronized int hashCode()
330
  {
331
    int hash = lastRule.hashCode();
332
    // FIXME - hash transitions?
333
    return hash;
334
  }
335
 
336
  public synchronized boolean equals(Object o)
337
  {
338
    if (! hasSameRules((TimeZone) o))
339
      return false;
340
 
341
    ZoneInfo zone = (ZoneInfo) o;
342
    return getID().equals(zone.getID());
343
  }
344
 
345
  /**
346
   * Test if the other time zone uses the same rule and only
347
   * possibly differs in ID.  This implementation for this particular
348
   * class will return true if the other object is a ZoneInfo,
349
   * the raw offsets and useDaylight are identical and if useDaylight
350
   * is true, also the start and end datas are identical.
351
   * @return true if this zone uses the same rule.
352
   */
353
  public boolean hasSameRules(TimeZone o)
354
  {
355
    if (this == o)
356
      return true;
357
    if (! (o instanceof ZoneInfo))
358
      return false;
359
    ZoneInfo zone = (ZoneInfo) o;
360
    if (zone.hashCode() != hashCode() || rawOffset != zone.rawOffset)
361
      return false;
362
    if (! lastRule.equals(zone.lastRule))
363
      return false;
364
    // FIXME - compare transitions?
365
    return true;
366
  }
367
 
368
  /**
369
   * Returns a string representation of this ZoneInfo object.
370
   * @return a string representation of this ZoneInfo object.
371
   */
372
  public String toString()
373
  {
374
    return getClass().getName() + "[" + "id=" + getID() + ",offset="
375
           + rawOffset + ",transitions=" + transitions.length
376
           + ",useDaylight=" + useDaylight
377
           + (useDaylight ? (",dstSavings=" + dstSavings) : "")
378
           + ",lastRule=" + lastRule.toString() + "]";
379
  }
380
 
381
  /**
382
   * Reads zic(8) compiled timezone data file from file
383
   * and returns a TimeZone class describing it, either
384
   * SimpleTimeZone or ZoneInfo depending on whether
385
   * it can be described by SimpleTimeZone rule or not.
386
   */
387
  public static TimeZone readTZFile(String id, String file)
388
  {
389
    DataInputStream dis = null;
390
    try
391
      {
392
        FileInputStream fis = new FileInputStream(file);
393
        BufferedInputStream bis = new BufferedInputStream(fis);
394
        dis = new DataInputStream(bis);
395
 
396
        // Make sure we are reading a tzfile.
397
        byte[] tzif = new byte[5];
398
        dis.readFully(tzif);
399
        int tzif2 = 4;
400
        if (tzif[0] == 'T' && tzif[1] == 'Z'
401
            && tzif[2] == 'i' && tzif[3] == 'f')
402
          {
403
            if (tzif[4] >= '2')
404
              tzif2 = 8;
405
            // Reserved bytes
406
            skipFully(dis, 16 - 1);
407
          }
408
        else
409
          // Darwin has tzdata files that don't start with the TZif marker
410
          skipFully(dis, 16 - 5);
411
 
412
        int ttisgmtcnt = dis.readInt();
413
        int ttisstdcnt = dis.readInt();
414
        int leapcnt = dis.readInt();
415
        int timecnt = dis.readInt();
416
        int typecnt = dis.readInt();
417
        int charcnt = dis.readInt();
418
        if (tzif2 == 8)
419
          {
420
            skipFully(dis, timecnt * (4 + 1) + typecnt * (4 + 1 + 1) + charcnt
421
                           + leapcnt * (4 + 4) + ttisgmtcnt + ttisstdcnt);
422
 
423
            dis.readFully(tzif);
424
            if (tzif[0] != 'T' || tzif[1] != 'Z' || tzif[2] != 'i'
425
                || tzif[3] != 'f' || tzif[4] < '2')
426
              return null;
427
 
428
            // Reserved bytes
429
            skipFully(dis, 16 - 1);
430
            ttisgmtcnt = dis.readInt();
431
            ttisstdcnt = dis.readInt();
432
            leapcnt = dis.readInt();
433
            timecnt = dis.readInt();
434
            typecnt = dis.readInt();
435
            charcnt = dis.readInt();
436
          }
437
 
438
        // Sanity checks
439
        if (typecnt <= 0 || timecnt < 0 || charcnt < 0
440
            || leapcnt < 0 || ttisgmtcnt < 0 || ttisstdcnt < 0
441
            || ttisgmtcnt > typecnt || ttisstdcnt > typecnt)
442
          return null;
443
 
444
        // Transition times
445
        long[] times = new long[timecnt];
446
        for (int i = 0; i < timecnt; i++)
447
          if (tzif2 == 8)
448
            times[i] = dis.readLong();
449
          else
450
            times[i] = (long) dis.readInt();
451
 
452
        // Transition types
453
        int[] types = new int[timecnt];
454
        for (int i = 0; i < timecnt; i++)
455
          {
456
            types[i] = dis.readByte();
457
            if (types[i] < 0)
458
              types[i] += 256;
459
            if (types[i] >= typecnt)
460
              return null;
461
          }
462
 
463
        // Types
464
        int[] offsets = new int[typecnt];
465
        int[] typeflags = new int[typecnt];
466
        for (int i = 0; i < typecnt; i++)
467
          {
468
            offsets[i] = dis.readInt();
469
            if (offsets[i] >= IS_DST / 2 || offsets[i] <= -IS_DST / 2)
470
              return null;
471
            int dst = dis.readByte();
472
            int abbrind = dis.readByte();
473
            if (abbrind < 0)
474
              abbrind += 256;
475
            if (abbrind >= charcnt)
476
              return null;
477
            typeflags[i] = (dst != 0 ? (1 << 8) : 0) + abbrind;
478
          }
479
 
480
        // Abbrev names
481
        byte[] names = new byte[charcnt];
482
        dis.readFully(names);
483
 
484
        // Leap transitions, for now ignore
485
        skipFully(dis, leapcnt * (tzif2 + 4) + ttisstdcnt + ttisgmtcnt);
486
 
487
        // tzIf2 format has optional POSIX TZ env string
488
        String tzstr = null;
489
        if (tzif2 == 8 && dis.readByte() == '\n')
490
          {
491
            tzstr = dis.readLine();
492
            if (tzstr.length() <= 0)
493
              tzstr = null;
494
          }
495
 
496
        // Get std/dst_offset and dst/non-dst time zone names.
497
        int std_ind = -1;
498
        int dst_ind = -1;
499
        if (timecnt == 0)
500
          std_ind = 0;
501
        else
502
          for (int i = timecnt - 1; i >= 0; i--)
503
            {
504
              if (std_ind == -1 && (typeflags[types[i]] & (1 << 8)) == 0)
505
                std_ind = types[i];
506
              else if (dst_ind == -1 && (typeflags[types[i]] & (1 << 8)) != 0)
507
                dst_ind = types[i];
508
              if (dst_ind != -1 && std_ind != -1)
509
                break;
510
            }
511
 
512
        if (std_ind == -1)
513
          return null;
514
 
515
        int j = typeflags[std_ind] & 255;
516
        while (j < charcnt && names[j] != 0)
517
          j++;
518
        String std_zonename = new String(names, typeflags[std_ind] & 255,
519
                                         j - (typeflags[std_ind] & 255),
520
                                         "ASCII");
521
 
522
        String dst_zonename = "";
523
        if (dst_ind != -1)
524
          {
525
            j = typeflags[dst_ind] & 255;
526
            while (j < charcnt && names[j] != 0)
527
              j++;
528
            dst_zonename = new String(names, typeflags[dst_ind] & 255,
529
                                      j - (typeflags[dst_ind] & 255), "ASCII");
530
          }
531
 
532
        // Only use gmt offset when necessary.
533
        // Also special case GMT+/- timezones.
534
        String std_offset_string = "";
535
        String dst_offset_string = "";
536
        if (tzstr == null
537
            && (dst_ind != -1
538
                || (offsets[std_ind] != 0
539
                    && !std_zonename.startsWith("GMT+")
540
                    && !std_zonename.startsWith("GMT-"))))
541
          {
542
            std_offset_string = Integer.toString(-offsets[std_ind] / 3600);
543
            int seconds = -offsets[std_ind] % 3600;
544
            if (seconds != 0)
545
              {
546
                if (seconds < 0)
547
                  seconds *= -1;
548
                if (seconds < 600)
549
                  std_offset_string += ":0" + Integer.toString(seconds / 60);
550
                else
551
                  std_offset_string += ":" + Integer.toString(seconds / 60);
552
                seconds = seconds % 60;
553
                if (seconds >= 10)
554
                  std_offset_string += ":" + Integer.toString(seconds);
555
                else if (seconds > 0)
556
                  std_offset_string += ":0" + Integer.toString(seconds);
557
              }
558
 
559
            if (dst_ind != -1 && offsets[dst_ind] != offsets[std_ind] + 3600)
560
              {
561
                dst_offset_string = Integer.toString(-offsets[dst_ind] / 3600);
562
                seconds = -offsets[dst_ind] % 3600;
563
                if (seconds != 0)
564
                  {
565
                    if (seconds < 0)
566
                      seconds *= -1;
567
                    if (seconds < 600)
568
                      dst_offset_string
569
                        += ":0" + Integer.toString(seconds / 60);
570
                    else
571
                      dst_offset_string
572
                        += ":" + Integer.toString(seconds / 60);
573
                    seconds = seconds % 60;
574
                    if (seconds >= 10)
575
                      dst_offset_string += ":" + Integer.toString(seconds);
576
                    else if (seconds > 0)
577
                      dst_offset_string += ":0" + Integer.toString(seconds);
578
                  }
579
              }
580
          }
581
 
582
        /*
583
         * If no tzIf2 POSIX TZ string is available and the timezone
584
         * uses DST, try to guess the last rule by trying to make
585
         * sense from transitions at 5 years in the future and onwards.
586
         * tzdata actually uses only 3 forms of rules:
587
         * fixed date within a month, e.g. change on April, 5th
588
         * 1st weekday on or after Nth: change on Sun>=15 in April
589
         * last weekday in a month: change on lastSun in April
590
         */
591
        String[] change_spec = { null, null };
592
        if (tzstr == null && dst_ind != -1 && timecnt > 10)
593
          {
594
            long nowPlus5y = System.currentTimeMillis() / 1000
595
                             + 5 * 365 * 86400;
596
            int first;
597
 
598
            for (first = timecnt - 1; first >= 0; first--)
599
              if (times[first] < nowPlus5y
600
                  || (types[first] != std_ind && types[first] != dst_ind)
601
                  || types[first] != types[timecnt - 2 + ((first ^ timecnt) & 1)])
602
                break;
603
            first++;
604
 
605
            if (timecnt - first >= 10 && types[timecnt - 1] != types[timecnt - 2])
606
              {
607
                GregorianCalendar cal
608
                  = new GregorianCalendar(new SimpleTimeZone(0, "GMT"));
609
 
610
                int[] values = new int[2 * 11];
611
                int i;
612
                for (i = timecnt - 1; i >= first; i--)
613
                  {
614
                    int base = (i % 2) * 11;
615
                    int offset = offsets[types[i > first ? i - 1 : i + 1]];
616
                    cal.setTimeInMillis((times[i] + offset) * 1000);
617
                    if (i >= timecnt - 2)
618
                      {
619
                        values[base + 0] = cal.get(Calendar.YEAR);
620
                        values[base + 1] = cal.get(Calendar.MONTH);
621
                        values[base + 2] = cal.get(Calendar.DAY_OF_MONTH);
622
                        values[base + 3]
623
                          = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
624
                        values[base + 4] = cal.get(Calendar.DAY_OF_WEEK);
625
                        values[base + 5] = cal.get(Calendar.HOUR_OF_DAY);
626
                        values[base + 6] = cal.get(Calendar.MINUTE);
627
                        values[base + 7] = cal.get(Calendar.SECOND);
628
                        values[base + 8] = values[base + 2]; // Range start
629
                        values[base + 9] = values[base + 2]; // Range end
630
                        values[base + 10] = 0; // Determined type
631
                      }
632
                    else
633
                      {
634
                        int year = cal.get(Calendar.YEAR);
635
                        int month = cal.get(Calendar.MONTH);
636
                        int day_of_month = cal.get(Calendar.DAY_OF_MONTH);
637
                        int month_days
638
                          = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
639
                        int day_of_week = cal.get(Calendar.DAY_OF_WEEK);
640
                        int hour = cal.get(Calendar.HOUR_OF_DAY);
641
                        int minute = cal.get(Calendar.MINUTE);
642
                        int second = cal.get(Calendar.SECOND);
643
                        if (year != values[base + 0] - 1
644
                            || month != values[base + 1]
645
                            || hour != values[base + 5]
646
                            || minute != values[base + 6]
647
                            || second != values[base + 7])
648
                          break;
649
                        if (day_of_week == values[base + 4])
650
                          {
651
                            // Either a Sun>=8 or lastSun rule.
652
                            if (day_of_month < values[base + 8])
653
                              values[base + 8] = day_of_month;
654
                            if (day_of_month > values[base + 9])
655
                              values[base + 9] = day_of_month;
656
                            if (values[base + 10] < 0)
657
                              break;
658
                            if (values[base + 10] == 0)
659
                              {
660
                                values[base + 10] = 1;
661
                                // If day of month > 28, this is
662
                                // certainly lastSun rule.
663
                                if (values[base + 2] > 28)
664
                                  values[base + 2] = 3;
665
                                // If day of month isn't in the last
666
                                // week, it can't be lastSun rule.
667
                                else if (values[base + 2]
668
                                         <= values[base + 3] - 7)
669
                                  values[base + 3] = 2;
670
                              }
671
                            if (values[base + 10] == 1)
672
                              {
673
                                // If day of month is > 28, this is
674
                                // certainly lastSun rule.
675
                                if (day_of_month > 28)
676
                                  values[base + 10] = 3;
677
                                // If day of month isn't in the last
678
                                // week, it can't be lastSun rule.
679
                                else if (day_of_month <= month_days - 7)
680
                                  values[base + 10] = 2;
681
                              }
682
                            else if ((values[base + 10] == 2
683
                                      && day_of_month > 28)
684
                                     || (values[base + 10] == 3
685
                                         && day_of_month <= month_days - 7))
686
                              break;
687
                          }
688
                        else
689
                          {
690
                            // Must be fixed day in month rule.
691
                            if (day_of_month != values[base + 2]
692
                                || values[base + 10] > 0)
693
                              break;
694
                            values[base + 4] = day_of_week;
695
                            values[base + 10] = -1;
696
                          }
697
                        values[base + 0] -= 1;
698
                      }
699
                  }
700
 
701
                if (i < first)
702
                  {
703
                    for (i = 0; i < 2; i++)
704
                      {
705
                        int base = 11 * i;
706
                        if (values[base + 10] == 0)
707
                          continue;
708
                        if (values[base + 10] == -1)
709
                          {
710
                            int[] dayCount
711
                              = { 0, 31, 59, 90, 120, 151,
712
                                  181, 212, 243, 273, 304, 334 };
713
                            int d = dayCount[values[base + 1]
714
                                             - Calendar.JANUARY];
715
                            d += values[base + 2];
716
                            change_spec[i] = ",J" + Integer.toString(d);
717
                          }
718
                        else if (values[base + 10] == 2)
719
                          {
720
                            // If we haven't seen all days of the week,
721
                            // we can't be sure what the rule really is.
722
                            if (values[base + 8] + 6 != values[base + 9])
723
                              continue;
724
 
725
                            int d;
726
                            d = values[base + 1] - Calendar.JANUARY + 1;
727
                            // E.g. Sun >= 5 is not representable in POSIX
728
                            // TZ env string, use ",Am.n.d" extension
729
                            // where m is month 1 .. 12, n is the date on
730
                            // or after which it happens and d is day
731
                            // of the week, 0 .. 6.  So Sun >= 5 in April
732
                            // is ",A4.5.0".
733
                            if ((values[base + 8] % 7) == 1)
734
                              {
735
                                change_spec[i] = ",M" + Integer.toString(d);
736
                                d = (values[base + 8] + 6) / 7;
737
                              }
738
                            else
739
                              {
740
                                change_spec[i] = ",A" + Integer.toString(d);
741
                                d = values[base + 8];
742
                              }
743
                            change_spec[i] += "." + Integer.toString(d);
744
                            d = values[base + 4] - Calendar.SUNDAY;
745
                            change_spec[i] += "." + Integer.toString(d);
746
                          }
747
                        else
748
                          {
749
                            // If we don't know whether this is lastSun or
750
                            // Sun >= 22 rule.  That can be either because
751
                            // there was insufficient number of
752
                            // transitions, or February, where it is quite
753
                            // probable we haven't seen any 29th dates.
754
                            // For February, assume lastSun rule, otherwise
755
                            // punt.
756
                            if (values[base + 10] == 1
757
                                && values[base + 1] != Calendar.FEBRUARY)
758
                              continue;
759
 
760
                            int d;
761
                            d = values[base + 1] - Calendar.JANUARY + 1;
762
                            change_spec[i] = ",M" + Integer.toString(d);
763
                            d = values[base + 4] - Calendar.SUNDAY;
764
                            change_spec[i] += ".5." + Integer.toString(d);
765
                          }
766
 
767
                        // Don't add time specification if time is
768
                        // 02:00:00.
769
                        if (values[base + 5] != 2
770
                            || values[base + 6] != 0
771
                            || values[base + 7] != 0)
772
                          {
773
                            int d = values[base + 5];
774
                            change_spec[i] += "/" + Integer.toString(d);
775
                            if (values[base + 6] != 0 || values[base + 7] != 0)
776
                              {
777
                                d = values[base + 6];
778
                                if (d < 10)
779
                                  change_spec[i]
780
                                    += ":0" + Integer.toString(d);
781
                                else
782
                                  change_spec[i] += ":" + Integer.toString(d);
783
                                d = values[base + 7];
784
                                if (d >= 10)
785
                                   change_spec[i]
786
                                     += ":" + Integer.toString(d);
787
                                else if (d > 0)
788
                                  change_spec[i]
789
                                    += ":0" + Integer.toString(d);
790
                              }
791
                          }
792
                      }
793
                    if (types[(timecnt - 1) & -2] == std_ind)
794
                      {
795
                        String tmp = change_spec[0];
796
                        change_spec[0] = change_spec[1];
797
                        change_spec[1] = tmp;
798
                      }
799
                  }
800
              }
801
          }
802
 
803
        if (tzstr == null)
804
          {
805
            tzstr = std_zonename + std_offset_string;
806
            if (change_spec[0] != null && change_spec[1] != null)
807
              tzstr += dst_zonename + dst_offset_string
808
                       + change_spec[0] + change_spec[1];
809
          }
810
 
811
        if (timecnt == 0)
812
          return new SimpleTimeZone(offsets[std_ind] * 1000,
813
                                    id != null ? id : tzstr);
814
 
815
        SimpleTimeZone endRule = createLastRule(tzstr);
816
        if (endRule == null)
817
          return null;
818
 
819
        /* Finally adjust the times array into the form the constructor
820
         * expects.  times[0] is special, the offset and DST flag there
821
         * are for all times before that transition.  Use the first non-DST
822
         * type.  For all other transitions, the data file has the type
823
         * (<offset, isdst, zonename>) for the time interval starting
824
         */
825
        for (int i = 0; i < typecnt; i++)
826
          if ((typeflags[i] & (1 << 8)) == 0)
827
            {
828
              times[0] = (times[0] << SECS_SHIFT) | (offsets[i] & OFFSET_MASK);
829
              break;
830
            }
831
 
832
        for (int i = 1; i < timecnt; i++)
833
          times[i] = (times[i] << SECS_SHIFT)
834
                     | (offsets[types[i - 1]] & OFFSET_MASK)
835
                     | ((typeflags[types[i - 1]] & (1 << 8)) != 0 ? IS_DST : 0);
836
 
837
        return new ZoneInfo(offsets[std_ind] * 1000, id != null ? id : tzstr,
838
                            times, endRule);
839
      }
840
    catch (IOException ioe)
841
      {
842
        // Parse error, not a proper tzfile.
843
        return null;
844
      }
845
    finally
846
      {
847
        try
848
          {
849
            if (dis != null)
850
              dis.close();
851
          }
852
        catch(IOException ioe)
853
          {
854
            // Error while close, nothing we can do.
855
          }
856
      }
857
  }
858
 
859
  /**
860
   * Skips the requested number of bytes in the given InputStream.
861
   * Throws EOFException if not enough bytes could be skipped.
862
   * Negative numbers of bytes to skip are ignored.
863
   */
864
  private static void skipFully(InputStream is, long l) throws IOException
865
  {
866
    while (l > 0)
867
      {
868
        long k = is.skip(l);
869
        if (k <= 0)
870
          throw new EOFException();
871
        l -= k;
872
      }
873
  }
874
 
875
  /**
876
   * Create a SimpleTimeZone from a POSIX TZ environment string,
877
   * see http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap08.html
878
   * for details.
879
   * It supports also an extension, where Am.n.d rule (m 1 .. 12, n 1 .. 25, d
880
   * 0 .. 6) describes day of week d on or after nth day of month m.
881
   * Say A4.5.0 is Sun>=5 in April.
882
   */
883
  private static SimpleTimeZone createLastRule(String tzstr)
884
  {
885
    String stdName = null;
886
    int stdOffs;
887
    int dstOffs;
888
    try
889
      {
890
        int idLength = tzstr.length();
891
 
892
        int index = 0;
893
        int prevIndex;
894
        char c;
895
 
896
        // get std
897
        do
898
          c = tzstr.charAt(index);
899
        while (c != '+' && c != '-' && c != ',' && c != ':'
900
               && ! Character.isDigit(c) && c != '\0' && ++index < idLength);
901
 
902
        if (index >= idLength)
903
          return new SimpleTimeZone(0, tzstr);
904
 
905
        stdName = tzstr.substring(0, index);
906
        prevIndex = index;
907
 
908
        // get the std offset
909
        do
910
          c = tzstr.charAt(index++);
911
        while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c))
912
               && index < idLength);
913
        if (index < idLength)
914
          index--;
915
 
916
        { // convert the dst string to a millis number
917
            String offset = tzstr.substring(prevIndex, index);
918
            prevIndex = index;
919
 
920
            if (offset.charAt(0) == '+' || offset.charAt(0) == '-')
921
              stdOffs = parseTime(offset.substring(1));
922
            else
923
              stdOffs = parseTime(offset);
924
 
925
            if (offset.charAt(0) == '-')
926
              stdOffs = -stdOffs;
927
 
928
            // TZ timezone offsets are positive when WEST of the meridian.
929
            stdOffs = -stdOffs;
930
        }
931
 
932
        // Done yet? (Format: std offset)
933
        if (index >= idLength)
934
          return new SimpleTimeZone(stdOffs, stdName);
935
 
936
        // get dst
937
        do
938
          c = tzstr.charAt(index);
939
        while (c != '+' && c != '-' && c != ',' && c != ':'
940
               && ! Character.isDigit(c) && c != '\0' && ++index < idLength);
941
 
942
        // Done yet? (Format: std offset dst)
943
        if (index >= idLength)
944
          return new SimpleTimeZone(stdOffs, stdName);
945
 
946
        // get the dst offset
947
        prevIndex = index;
948
        do
949
          c = tzstr.charAt(index++);
950
        while ((c == '-' || c == '+' || c == ':' || Character.isDigit(c))
951
               && index < idLength);
952
        if (index < idLength)
953
          index--;
954
 
955
        if (index == prevIndex && (c == ',' || c == ';'))
956
          {
957
            // Missing dst offset defaults to one hour ahead of standard
958
            // time.
959
            dstOffs = stdOffs + 60 * 60 * 1000;
960
          }
961
        else
962
          { // convert the dst string to a millis number
963
            String offset = tzstr.substring(prevIndex, index);
964
            prevIndex = index;
965
 
966
            if (offset.charAt(0) == '+' || offset.charAt(0) == '-')
967
              dstOffs = parseTime(offset.substring(1));
968
            else
969
              dstOffs = parseTime(offset);
970
 
971
            if (offset.charAt(0) == '-')
972
              dstOffs = -dstOffs;
973
 
974
            // TZ timezone offsets are positive when WEST of the meridian.
975
            dstOffs = -dstOffs;
976
          }
977
 
978
        // Done yet? (Format: std offset dst offset)
979
        if (index >= idLength)
980
          return new SimpleTimeZone(stdOffs, stdName);
981
 
982
        // get the DST rule
983
        if (tzstr.charAt(index) == ','
984
            || tzstr.charAt(index) == ';')
985
          {
986
            index++;
987
            int offs = index;
988
            while (tzstr.charAt(index) != ','
989
                   && tzstr.charAt(index) != ';')
990
              index++;
991
            String startTime = tzstr.substring(offs, index);
992
            index++;
993
            String endTime = tzstr.substring(index);
994
 
995
            index = startTime.indexOf('/');
996
            int startMillis;
997
            int endMillis;
998
            String startDate;
999
            String endDate;
1000
            if (index != -1)
1001
              {
1002
                startDate = startTime.substring(0, index);
1003
                startMillis = parseTime(startTime.substring(index + 1));
1004
              }
1005
            else
1006
              {
1007
                startDate = startTime;
1008
                // if time isn't given, default to 2:00:00 AM.
1009
                startMillis = 2 * 60 * 60 * 1000;
1010
              }
1011
            index = endTime.indexOf('/');
1012
            if (index != -1)
1013
              {
1014
                endDate = endTime.substring(0, index);
1015
                endMillis = parseTime(endTime.substring(index + 1));
1016
              }
1017
            else
1018
              {
1019
                endDate = endTime;
1020
                // if time isn't given, default to 2:00:00 AM.
1021
                endMillis = 2 * 60 * 60 * 1000;
1022
              }
1023
 
1024
            int[] start = getDateParams(startDate);
1025
            int[] end = getDateParams(endDate);
1026
            return new SimpleTimeZone(stdOffs, tzstr, start[0], start[1],
1027
                                      start[2], startMillis, end[0], end[1],
1028
                                      end[2], endMillis, (dstOffs - stdOffs));
1029
          }
1030
      }
1031
 
1032
    catch (IndexOutOfBoundsException _)
1033
      {
1034
      }
1035
    catch (NumberFormatException _)
1036
      {
1037
      }
1038
 
1039
    return null;
1040
  }
1041
 
1042
  /**
1043
   * Parses and returns the params for a POSIX TZ date field,
1044
   * in the format int[]{ month, day, dayOfWeek }, following the
1045
   * SimpleTimeZone constructor rules.
1046
   */
1047
  private static int[] getDateParams(String date)
1048
  {
1049
    int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
1050
    int month;
1051
    int type = 0;
1052
 
1053
    if (date.charAt(0) == 'M' || date.charAt(0) == 'm')
1054
      type = 1;
1055
    else if (date.charAt(0) == 'A' || date.charAt(0) == 'a')
1056
      type = 2;
1057
 
1058
    if (type > 0)
1059
      {
1060
        int day;
1061
 
1062
        // Month, week of month, day of week
1063
        // "Mm.w.d".  d is between 0 (Sunday) and 6.  Week w is
1064
        // between 1 and 5; Week 1 is the first week in which day d
1065
        // occurs and Week 5 specifies the last d day in the month.
1066
        // Month m is between 1 and 12.
1067
 
1068
        // Month, day of month, day of week
1069
        // ZoneInfo extension, not in POSIX
1070
        // "Am.n.d".  d is between 0 (Sunday) and 6.  Day of month n is
1071
        // between 1 and 25.  Month m is between 1 and 12.
1072
 
1073
        month = Integer.parseInt(date.substring(1, date.indexOf('.')));
1074
        int week = Integer.parseInt(date.substring(date.indexOf('.') + 1,
1075
                                                   date.lastIndexOf('.')));
1076
        int dayOfWeek = Integer.parseInt(date.substring(date.lastIndexOf('.')
1077
                                                        + 1));
1078
        dayOfWeek++; // Java day of week is one-based, Sunday is first day.
1079
 
1080
        if (type == 2)
1081
          {
1082
            day = week;
1083
            dayOfWeek = -dayOfWeek;
1084
          }
1085
        else if (week == 5)
1086
          day = -1; // last day of month is -1 in java, 5 in TZ
1087
        else
1088
          {
1089
            // First day of week starting on or after.  For example,
1090
            // to specify the second Sunday of April, set month to
1091
            // APRIL, day-of-month to 8, and day-of-week to -SUNDAY.
1092
            day = (week - 1) * 7 + 1;
1093
            dayOfWeek = -dayOfWeek;
1094
          }
1095
 
1096
        month--; // Java month is zero-based.
1097
        return new int[] { month, day, dayOfWeek };
1098
      }
1099
 
1100
    // julian day, either zero-based 0<=n<=365 (incl feb 29)
1101
    // or one-based 1<=n<=365 (no feb 29)
1102
    int julianDay; // Julian day
1103
 
1104
    if (date.charAt(0) != 'J' || date.charAt(0) != 'j')
1105
      {
1106
        julianDay = Integer.parseInt(date.substring(1));
1107
        julianDay++; // make 1-based
1108
        // Adjust day count to include feb 29.
1109
        dayCount = new int[]
1110
                   {
1111
                     0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335
1112
                   };
1113
      }
1114
    else
1115
      // 1-based julian day
1116
      julianDay = Integer.parseInt(date);
1117
 
1118
    int i = 11;
1119
    while (i > 0)
1120
      if (dayCount[i] < julianDay)
1121
        break;
1122
      else
1123
        i--;
1124
    julianDay -= dayCount[i];
1125
    month = i;
1126
    return new int[] { month, julianDay, 0 };
1127
  }
1128
 
1129
  /**
1130
   * Parses a time field hh[:mm[:ss]], returning the result
1131
   * in milliseconds. No leading sign.
1132
   */
1133
  private static int parseTime(String time)
1134
  {
1135
    int millis = 0;
1136
    int i = 0;
1137
 
1138
    while (i < time.length())
1139
      if (time.charAt(i) == ':')
1140
        break;
1141
      else
1142
        i++;
1143
    millis = 60 * 60 * 1000 * Integer.parseInt(time.substring(0, i));
1144
    if (i >= time.length())
1145
      return millis;
1146
 
1147
    int iprev = ++i;
1148
    while (i < time.length())
1149
      if (time.charAt(i) == ':')
1150
        break;
1151
      else
1152
        i++;
1153
    millis += 60 * 1000 * Integer.parseInt(time.substring(iprev, i));
1154
    if (i >= time.length())
1155
      return millis;
1156
 
1157
    millis += 1000 * Integer.parseInt(time.substring(++i));
1158
    return millis;
1159
  }
1160
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.