MonthWeekEventsView.java revision 96a0b5436f79157e8175b3b761fd5ccfe26a7f62
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.calendar.month; 18 19import com.android.calendar.Event; 20import com.android.calendar.R; 21import com.android.calendar.Utils; 22 23import android.content.Context; 24import android.content.res.Configuration; 25import android.content.res.Resources; 26import android.graphics.Canvas; 27import android.graphics.Paint; 28import android.graphics.Paint.Align; 29import android.graphics.Paint.Style; 30import android.graphics.Typeface; 31import android.graphics.drawable.Drawable; 32import android.text.TextPaint; 33import android.text.TextUtils; 34import android.text.format.DateUtils; 35import android.text.format.Time; 36import android.util.Log; 37 38import java.util.ArrayList; 39import java.util.Arrays; 40import java.util.Formatter; 41import java.util.HashMap; 42import java.util.Iterator; 43import java.util.List; 44import java.util.Locale; 45 46public class MonthWeekEventsView extends SimpleWeekView { 47 private static final String TAG = "MonthView"; 48 49 public static final String VIEW_PARAMS_ORIENTATION = "orientation"; 50 51 private static int TEXT_SIZE_MONTH_NUMBER = 32; 52 private static int TEXT_SIZE_EVENT = 14; 53 private static int TEXT_SIZE_MORE_EVENTS = 12; 54 private static int TEXT_SIZE_MONTH_NAME = 14; 55 private static int TEXT_SIZE_WEEK_NUM = 12; 56 57 private static final int DEFAULT_EDGE_SPACING = 4; 58 private static int PADDING_MONTH_NUMBER = 4; 59 private static int PADDING_WEEK_NUMBER = 16; 60 private static int DAY_SEPARATOR_OUTER_WIDTH = 5; 61 private static int DAY_SEPARATOR_INNER_WIDTH = 1; 62 private static int DAY_SEPARATOR_VERTICAL_LENGTH = 53; 63 private static int DAY_SEPARATOR_VERTICAL_LENGHT_PORTRAIT = 64; 64 65 private static int EVENT_X_OFFSET_LANDSCAPE = 44; 66 private static int EVENT_Y_OFFSET_LANDSCAPE = 11; 67 private static int EVENT_Y_OFFSET_PORTRAIT = 18; 68 private static int EVENT_SQUARE_WIDTH = 10; 69 private static int EVENT_SQUARE_BORDER = 1; 70 private static int EVENT_LINE_PADDING = 4; 71 private static int EVENT_RIGHT_PADDING = 4; 72 private static int EVENT_BOTTOM_PADDING = 15; 73 74 private static int SPACING_WEEK_NUMBER = 19; 75 private static boolean mScaled = false; 76 private boolean mIsMultipane; 77 78 protected Time mToday = new Time(); 79 protected boolean mHasToday = false; 80 protected int mTodayIndex = -1; 81 protected int mOrientation = Configuration.ORIENTATION_LANDSCAPE; 82 protected List<ArrayList<Event>> mEvents = null; 83 // This is for drawing the outlines around event chips and supports up to 10 84 // events being drawn on each day. The code will expand this if necessary. 85 protected FloatRef mEventOutlines = new FloatRef(10 * 4 * 4 * 7); 86 87 protected static StringBuilder mStringBuilder = new StringBuilder(50); 88 // TODO recreate formatter when locale changes 89 protected static Formatter mFormatter = new Formatter(mStringBuilder, Locale.getDefault()); 90 91 protected Paint mMonthNamePaint; 92 protected TextPaint mEventPaint; 93 protected TextPaint mEventExtrasPaint; 94 protected Paint mWeekNumPaint; 95 96 protected Drawable mTodayDrawable; 97 98 protected int mMonthNumHeight; 99 protected int mEventHeight; 100 protected int mExtrasHeight; 101 protected int mWeekNumHeight; 102 103 protected int mMonthNumColor; 104 protected int mMonthNumOtherColor; 105 protected int mMonthNumTodayColor; 106 protected int mMonthNameColor; 107 protected int mMonthNameOtherColor; 108 protected int mMonthEventColor; 109 protected int mMonthEventExtraColor; 110 protected int mMonthEventOtherColor; 111 protected int mMonthEventExtraOtherColor; 112 protected int mMonthWeekNumColor; 113 114 protected int mEventChipOutlineColor = 0xFFFFFFFF; 115 protected int mDaySeparatorOuterColor = 0x33FFFFFF; 116 protected int mDaySeparatorInnerColor = 0x1A000000; 117 118 /** 119 * This provides a reference to a float array which allows for easy size 120 * checking and reallocation. Used for drawing lines. 121 */ 122 private class FloatRef { 123 float[] array; 124 125 public FloatRef(int size) { 126 array = new float[size]; 127 } 128 129 public void ensureSize(int newSize) { 130 if (newSize >= array.length) { 131 // Add enough space for 7 more boxes to be drawn 132 array = Arrays.copyOf(array, newSize + 16 * 7); 133 } 134 } 135 } 136 137 /** 138 * @param context 139 */ 140 public MonthWeekEventsView(Context context) { 141 super(context); 142 143 Resources resources = context.getResources(); 144 TEXT_SIZE_MONTH_NUMBER = resources.getInteger(R.integer.text_size_month_number); 145 146 mPadding = DEFAULT_EDGE_SPACING; 147 if (mScale != 1 && !mScaled) { 148 PADDING_MONTH_NUMBER *= mScale; 149 PADDING_WEEK_NUMBER *= mScale; 150 SPACING_WEEK_NUMBER *= mScale; 151 TEXT_SIZE_MONTH_NUMBER *= mScale; 152 TEXT_SIZE_EVENT *= mScale; 153 TEXT_SIZE_MORE_EVENTS *= mScale; 154 TEXT_SIZE_MONTH_NAME *= mScale; 155 TEXT_SIZE_WEEK_NUM *= mScale; 156 DAY_SEPARATOR_OUTER_WIDTH *= mScale; 157 DAY_SEPARATOR_INNER_WIDTH *= mScale; 158 DAY_SEPARATOR_VERTICAL_LENGTH *= mScale; 159 DAY_SEPARATOR_VERTICAL_LENGHT_PORTRAIT *= mScale; 160 EVENT_X_OFFSET_LANDSCAPE *= mScale; 161 EVENT_Y_OFFSET_LANDSCAPE *= mScale; 162 EVENT_Y_OFFSET_PORTRAIT *= mScale; 163 EVENT_SQUARE_WIDTH *= mScale; 164 EVENT_LINE_PADDING *= mScale; 165 EVENT_BOTTOM_PADDING *= mScale; 166 EVENT_RIGHT_PADDING *= mScale; 167 mPadding = (int) (DEFAULT_EDGE_SPACING * mScale); 168 mScaled = true; 169 } 170 mIsMultipane = Utils.isMultiPaneConfiguration (context); 171 } 172 173 public void setEvents(List<ArrayList<Event>> sortedEvents) { 174 mEvents = sortedEvents; 175 if (sortedEvents == null) { 176 return; 177 } 178 if (sortedEvents.size() != mNumDays) { 179 if (Log.isLoggable(TAG, Log.ERROR)) { 180 Log.wtf(TAG, "Events size must be same as days displayed: size=" 181 + sortedEvents.size() + " days=" + mNumDays); 182 } 183 mEvents = null; 184 return; 185 } 186 } 187 188 protected void loadColors(Context context) { 189 Resources res = context.getResources(); 190 mMonthWeekNumColor = res.getColor(R.color.month_week_num_color); 191 mMonthNumColor = res.getColor(R.color.month_day_number); 192 mMonthNumOtherColor = res.getColor(R.color.month_day_number_other); 193 mMonthNumTodayColor = res.getColor(R.color.month_today_number); 194 mMonthNameColor = mMonthNumColor; 195 mMonthNameOtherColor = mMonthNumOtherColor; 196 mMonthEventColor = res.getColor(R.color.month_event_color); 197 mMonthEventExtraColor = res.getColor(R.color.month_event_extra_color); 198 mMonthEventOtherColor = res.getColor(R.color.month_event_other_color); 199 mMonthEventExtraOtherColor = res.getColor(R.color.month_event_extra_other_color); 200 201 mTodayDrawable = res.getDrawable(R.drawable.today_blue_week_holo_light); 202 } 203 204 /** 205 * Sets up the text and style properties for painting. Override this if you 206 * want to use a different paint. 207 */ 208 @Override 209 protected void setPaintProperties() { 210 loadColors(mContext); 211 // TODO modify paint properties depending on isMini 212 p.setStyle(Style.FILL); 213 214 mMonthNumPaint = new Paint(); 215 mMonthNumPaint.setFakeBoldText(false); 216 mMonthNumPaint.setAntiAlias(true); 217 mMonthNumPaint.setTextSize(TEXT_SIZE_MONTH_NUMBER); 218 mMonthNumPaint.setColor(mMonthNumColor); 219 mMonthNumPaint.setStyle(Style.FILL); 220 mMonthNumPaint.setTextAlign(Align.LEFT); 221 mMonthNumPaint.setTypeface(Typeface.DEFAULT_BOLD); 222 223 mMonthNumHeight = (int) (-mMonthNumPaint.ascent()); 224 225 mEventPaint = new TextPaint(); 226 mEventPaint.setFakeBoldText(false); 227 mEventPaint.setAntiAlias(true); 228 mEventPaint.setTextSize(TEXT_SIZE_EVENT); 229 mEventPaint.setColor(mMonthEventColor); 230 231 mEventHeight = (int) (-mEventPaint.ascent()); 232 233 mEventExtrasPaint = new TextPaint(); 234 mEventExtrasPaint.setFakeBoldText(false); 235 mEventExtrasPaint.setAntiAlias(true); 236 mEventExtrasPaint.setStrokeWidth(EVENT_SQUARE_BORDER); 237 mEventExtrasPaint.setTextSize(TEXT_SIZE_EVENT); 238 mEventExtrasPaint.setColor(mMonthEventExtraColor); 239 mEventExtrasPaint.setStyle(Style.FILL); 240 mEventExtrasPaint.setTextAlign(Align.LEFT); 241 242 mWeekNumPaint = new Paint(); 243 mWeekNumPaint.setFakeBoldText(false); 244 mWeekNumPaint.setAntiAlias(true); 245 mWeekNumPaint.setTextSize(TEXT_SIZE_WEEK_NUM); 246 mWeekNumPaint.setColor(mWeekNumColor); 247 mWeekNumPaint.setStyle(Style.FILL); 248 mWeekNumPaint.setTextAlign(Align.RIGHT); 249 250 mWeekNumHeight = (int) (-mWeekNumPaint.ascent()); 251 } 252 253 @Override 254 public void setWeekParams(HashMap<String, Integer> params, String tz) { 255 super.setWeekParams(params, tz); 256 257 if (params.containsKey(VIEW_PARAMS_ORIENTATION)) { 258 mOrientation = params.get(VIEW_PARAMS_ORIENTATION); 259 } 260 261 mToday.timezone = tz; 262 mToday.setToNow(); 263 mToday.normalize(true); 264 int julianToday = Time.getJulianDay(mToday.toMillis(false), mToday.gmtoff); 265 if (julianToday >= mFirstJulianDay && julianToday < mFirstJulianDay + mNumDays) { 266 mHasToday = true; 267 mTodayIndex = julianToday - mFirstJulianDay; 268 } else { 269 mHasToday = false; 270 mTodayIndex = -1; 271 } 272 mNumCells = mNumDays + 1; 273 } 274 275 @Override 276 protected void onDraw(Canvas canvas) { 277 drawBackground(canvas); 278 drawWeekNums(canvas); 279 drawDaySeparators(canvas); 280 if (mIsMultipane) { 281 drawEvents(canvas); 282 } 283 } 284 285 @Override 286 protected void drawDaySeparators(Canvas canvas) { 287 // mDaySeparatorOuterColor 288 float lines[] = new float[8 * 4]; 289 int count = 7 * 4; 290 int wkNumOffset = 0; 291 int effectiveWidth = mWidth - mPadding * 2; 292 count += 4; 293 wkNumOffset = 1; 294 effectiveWidth -= SPACING_WEEK_NUMBER; 295 lines[0] = mPadding; 296 lines[1] = DAY_SEPARATOR_OUTER_WIDTH / 2 + 1; 297 lines[2] = mWidth - mPadding; 298 lines[3] = lines[1]; 299 int y0 = DAY_SEPARATOR_OUTER_WIDTH / 2 + DAY_SEPARATOR_INNER_WIDTH; 300 int y1; 301 if (mOrientation == Configuration.ORIENTATION_PORTRAIT) { 302 y1 = y0 + DAY_SEPARATOR_VERTICAL_LENGHT_PORTRAIT; 303 } else { 304 y1 = y0 + DAY_SEPARATOR_VERTICAL_LENGTH; 305 } 306 307 for (int i = 4; i < count;) { 308 int x = (i / 4 - wkNumOffset) * effectiveWidth / (mNumDays) + mPadding 309 + (SPACING_WEEK_NUMBER * wkNumOffset); 310 lines[i++] = x; 311 lines[i++] = y0; 312 lines[i++] = x; 313 lines[i++] = y1; 314 } 315 p.setColor(mDaySeparatorOuterColor); 316 p.setStrokeWidth(DAY_SEPARATOR_OUTER_WIDTH); 317 canvas.drawLines(lines, 0, count, p); 318 p.setColor(mDaySeparatorInnerColor); 319 p.setStrokeWidth(DAY_SEPARATOR_INNER_WIDTH); 320 canvas.drawLines(lines, 0, count, p); 321 } 322 323 @Override 324 protected void drawBackground(Canvas canvas) { 325 if (mHasToday) { 326 p.setColor(mSelectedWeekBGColor); 327 } else { 328 return; 329 } 330 int wkNumOffset = 0; 331 int effectiveWidth = mWidth - mPadding * 2; 332 wkNumOffset = 1; 333 effectiveWidth -= SPACING_WEEK_NUMBER; 334 r.top = DAY_SEPARATOR_OUTER_WIDTH + 1; 335 r.bottom = mHeight; 336 r.left = (mTodayIndex) * effectiveWidth / (mNumDays) + mPadding 337 + (SPACING_WEEK_NUMBER * wkNumOffset) + DAY_SEPARATOR_OUTER_WIDTH / 2 + 1; 338 r.right = (mTodayIndex + 1) * effectiveWidth / (mNumDays) + mPadding 339 + (SPACING_WEEK_NUMBER * wkNumOffset) - DAY_SEPARATOR_OUTER_WIDTH / 2; 340 mTodayDrawable.setBounds(r); 341 mTodayDrawable.draw(canvas); 342 } 343 344 @Override 345 protected void drawWeekNums(Canvas canvas) { 346 int y; 347 348 int i = 0; 349 int offset = 0; 350 int effectiveWidth = mWidth - mPadding * 2; 351 int todayIndex = mTodayIndex; 352 int x = PADDING_WEEK_NUMBER + mPadding; 353 int numCount = mNumDays; 354 y = mWeekNumHeight + PADDING_MONTH_NUMBER; 355 if (mShowWeekNum) { 356 canvas.drawText(mDayNumbers[0], x, y, mWeekNumPaint); 357 numCount++; 358 i++; 359 todayIndex++; 360 offset++; 361 } 362 effectiveWidth -= SPACING_WEEK_NUMBER; 363 364 y = (mMonthNumHeight + PADDING_MONTH_NUMBER); 365 366 boolean isFocusMonth = mFocusDay[i]; 367 mMonthNumPaint.setColor(isFocusMonth ? mMonthNumColor : mMonthNumOtherColor); 368 for (; i < numCount; i++) { 369 if (mHasToday && todayIndex == i) { 370 mMonthNumPaint.setColor(mMonthNumTodayColor); 371 if (i + 1 < numCount) { 372 // Make sure the color will be set back on the next 373 // iteration 374 isFocusMonth = !mFocusDay[i + 1]; 375 } 376 } else if (mFocusDay[i] != isFocusMonth) { 377 isFocusMonth = mFocusDay[i]; 378 mMonthNumPaint.setColor(isFocusMonth ? mMonthNumColor : mMonthNumOtherColor); 379 } 380 x = (i - offset) * effectiveWidth / (mNumDays) + mPadding + PADDING_MONTH_NUMBER 381 + SPACING_WEEK_NUMBER; 382 canvas.drawText(mDayNumbers[i], x, y, mMonthNumPaint); 383 384 } 385 } 386 387 protected void drawEvents(Canvas canvas) { 388 if (mEvents == null) { 389 return; 390 } 391 int wkNumOffset = 0; 392 int effectiveWidth = mWidth - mPadding * 2; 393 wkNumOffset = 1; 394 effectiveWidth -= SPACING_WEEK_NUMBER; 395 396 int day = -1; 397 int outlineCount = 0; 398 for (ArrayList<Event> eventDay : mEvents) { 399 day++; 400 if (eventDay == null || eventDay.size() == 0) { 401 continue; 402 } 403 int ySquare; 404 int xSquare = day * effectiveWidth / (mNumDays) + mPadding 405 + (SPACING_WEEK_NUMBER * wkNumOffset); 406 if (mOrientation == Configuration.ORIENTATION_PORTRAIT) { 407 ySquare = EVENT_Y_OFFSET_PORTRAIT + mMonthNumHeight + PADDING_MONTH_NUMBER; 408 xSquare += PADDING_MONTH_NUMBER + 1; 409 } else { 410 ySquare = EVENT_Y_OFFSET_LANDSCAPE; 411 xSquare += EVENT_X_OFFSET_LANDSCAPE; 412 } 413 int rightEdge = (day + 1) * effectiveWidth / (mNumDays) + mPadding 414 + (SPACING_WEEK_NUMBER * wkNumOffset) - EVENT_RIGHT_PADDING; 415 int eventCount = 0; 416 Iterator<Event> iter = eventDay.iterator(); 417 while (iter.hasNext()) { 418 Event event = iter.next(); 419 int newY = drawEvent(canvas, event, xSquare, ySquare, rightEdge, iter.hasNext()); 420 if (newY == ySquare) { 421 break; 422 } 423 outlineCount = addChipOutline(mEventOutlines, outlineCount, xSquare, ySquare); 424 eventCount++; 425 ySquare = newY; 426 } 427 428 int remaining = eventDay.size() - eventCount; 429 if (remaining > 0) { 430 drawMoreEvents(canvas, remaining, xSquare); 431 } 432 } 433 if (outlineCount > 0) { 434 p.setColor(mEventChipOutlineColor); 435 p.setStrokeWidth(EVENT_SQUARE_BORDER); 436 canvas.drawLines(mEventOutlines.array, 0, outlineCount, p); 437 } 438 } 439 440 protected int addChipOutline(FloatRef lines, int count, int x, int y) { 441 lines.ensureSize(count + 16); 442 // top of box 443 lines.array[count++] = x; 444 lines.array[count++] = y; 445 lines.array[count++] = x + EVENT_SQUARE_WIDTH; 446 lines.array[count++] = y; 447 // right side of box 448 lines.array[count++] = x + EVENT_SQUARE_WIDTH; 449 lines.array[count++] = y; 450 lines.array[count++] = x + EVENT_SQUARE_WIDTH; 451 lines.array[count++] = y + EVENT_SQUARE_WIDTH; 452 // left side of box 453 lines.array[count++] = x; 454 lines.array[count++] = y; 455 lines.array[count++] = x; 456 lines.array[count++] = y + EVENT_SQUARE_WIDTH + 1; 457 // bottom of box 458 lines.array[count++] = x; 459 lines.array[count++] = y + EVENT_SQUARE_WIDTH; 460 lines.array[count++] = x + EVENT_SQUARE_WIDTH + 1; 461 lines.array[count++] = y + EVENT_SQUARE_WIDTH; 462 463 return count; 464 } 465 466 /** 467 * Attempts to draw the given event. Returns the y for the next event or the 468 * original y if the event will not fit. An event is considered to not fit 469 * if the event and its extras won't fit or if there are more events and the 470 * more events line would not fit after drawing this event. 471 * 472 * @param event the event to draw 473 * @param x the top left corner for this event's color chip 474 * @param y the top left corner for this event's color chip 475 * @return the y for the next event or the original y if it won't fit 476 */ 477 protected int drawEvent( 478 Canvas canvas, Event event, int x, int y, int rightEdge, boolean moreEvents) { 479 int requiredSpace = EVENT_LINE_PADDING + mEventHeight; 480 int multiplier = 1; 481 if (moreEvents) { 482 multiplier++; 483 } 484 if (!event.allDay) { 485 multiplier++; 486 } 487 requiredSpace *= multiplier; 488 if (requiredSpace + y >= mHeight - EVENT_BOTTOM_PADDING) { 489 // Not enough space, return 490 return y; 491 } 492 r.left = x; 493 r.right = x + EVENT_SQUARE_WIDTH; 494 r.top = y; 495 r.bottom = y + EVENT_SQUARE_WIDTH; 496 p.setColor(event.color); 497 canvas.drawRect(r, p); 498 499 int textX = x + EVENT_SQUARE_WIDTH + EVENT_LINE_PADDING; 500 int textY = y + mEventHeight - EVENT_LINE_PADDING / 2; 501 float avail = rightEdge - textX; 502 CharSequence text = TextUtils.ellipsize( 503 event.title, mEventPaint, avail, TextUtils.TruncateAt.END); 504 canvas.drawText(text.toString(), textX, textY, mEventPaint); 505 if (!event.allDay) { 506 textY += mEventHeight + EVENT_LINE_PADDING; 507 mStringBuilder.setLength(0); 508 text = DateUtils.formatDateRange(mContext, mFormatter, event.startMillis, 509 event.endMillis, DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL, 510 Utils.getTimeZone(mContext, null)).toString(); 511 text = TextUtils.ellipsize(text, mEventExtrasPaint, avail, TextUtils.TruncateAt.END); 512 canvas.drawText(text.toString(), textX, textY, mEventExtrasPaint); 513 } 514 515 return textY + EVENT_LINE_PADDING; 516 } 517 518 protected void drawMoreEvents(Canvas canvas, int remainingEvents, int x) { 519 FloatRef lines = new FloatRef(4 * 4); 520 int y = mHeight - EVENT_BOTTOM_PADDING + EVENT_LINE_PADDING / 2 - mEventHeight; 521 addChipOutline(lines, 0, x, y); 522 canvas.drawLines(lines.array, mEventExtrasPaint); 523 String text = mContext.getResources().getQuantityString( 524 R.plurals.month_more_events, remainingEvents); 525 y = mHeight - EVENT_BOTTOM_PADDING; 526 x += EVENT_SQUARE_WIDTH + EVENT_LINE_PADDING; 527 mEventExtrasPaint.setFakeBoldText(true); 528 canvas.drawText(String.format(text, remainingEvents), x, y, mEventExtrasPaint); 529 mEventExtrasPaint.setFakeBoldText(false); 530 } 531 532 @Override 533 protected void updateSelectionPositions() { 534 if (mHasSelectedDay) { 535 int selectedPosition = mSelectedDay - mWeekStart; 536 if (selectedPosition < 0) { 537 selectedPosition += 7; 538 } 539 int effectiveWidth = mWidth - mPadding * 2; 540 effectiveWidth -= SPACING_WEEK_NUMBER; 541 mSelectedLeft = selectedPosition * effectiveWidth / mNumDays + mPadding; 542 mSelectedRight = (selectedPosition + 1) * effectiveWidth / mNumDays + mPadding; 543 mSelectedLeft += SPACING_WEEK_NUMBER; 544 mSelectedRight += SPACING_WEEK_NUMBER; 545 } 546 } 547 548 @Override 549 public Time getDayFromLocation(float x) { 550 int dayStart = SPACING_WEEK_NUMBER + mPadding; 551 if (x < dayStart || x > mWidth - mPadding) { 552 return null; 553 } 554 // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels 555 int dayPosition = (int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding)); 556 int day = mFirstJulianDay + dayPosition; 557 558 Time time = new Time(mTimeZone); 559 if (mWeek == 0) { 560 // This week is weird... 561 if (day < Time.EPOCH_JULIAN_DAY) { 562 day++; 563 } else if (day == Time.EPOCH_JULIAN_DAY) { 564 time.set(1, 0, 1970); 565 time.normalize(true); 566 return time; 567 } 568 } 569 570 time.setJulianDay(day); 571 return time; 572 } 573 574} 575