1/* 2 * Copyright (C) 2006 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 android.text.format; 18 19import android.content.res.Resources; 20 21import java.util.Locale; 22import java.util.TimeZone; 23 24import libcore.icu.LocaleData; 25 26/** 27 * An alternative to the {@link java.util.Calendar} and 28 * {@link java.util.GregorianCalendar} classes. An instance of the Time class represents 29 * a moment in time, specified with second precision. It is modelled after 30 * struct tm, and in fact, uses struct tm to implement most of the 31 * functionality. 32 */ 33public class Time { 34 private static final String Y_M_D_T_H_M_S_000 = "%Y-%m-%dT%H:%M:%S.000"; 35 private static final String Y_M_D_T_H_M_S_000_Z = "%Y-%m-%dT%H:%M:%S.000Z"; 36 private static final String Y_M_D = "%Y-%m-%d"; 37 38 public static final String TIMEZONE_UTC = "UTC"; 39 40 /** 41 * The Julian day of the epoch, that is, January 1, 1970 on the Gregorian 42 * calendar. 43 */ 44 public static final int EPOCH_JULIAN_DAY = 2440588; 45 46 /** 47 * The Julian day of the Monday in the week of the epoch, December 29, 1969 48 * on the Gregorian calendar. 49 */ 50 public static final int MONDAY_BEFORE_JULIAN_EPOCH = EPOCH_JULIAN_DAY - 3; 51 52 /** 53 * True if this is an allDay event. The hour, minute, second fields are 54 * all zero, and the date is displayed the same in all time zones. 55 */ 56 public boolean allDay; 57 58 /** 59 * Seconds [0-61] (2 leap seconds allowed) 60 */ 61 public int second; 62 63 /** 64 * Minute [0-59] 65 */ 66 public int minute; 67 68 /** 69 * Hour of day [0-23] 70 */ 71 public int hour; 72 73 /** 74 * Day of month [1-31] 75 */ 76 public int monthDay; 77 78 /** 79 * Month [0-11] 80 */ 81 public int month; 82 83 /** 84 * Year. For example, 1970. 85 */ 86 public int year; 87 88 /** 89 * Day of week [0-6] 90 */ 91 public int weekDay; 92 93 /** 94 * Day of year [0-365] 95 */ 96 public int yearDay; 97 98 /** 99 * This time is in daylight savings time. One of: 100 * <ul> 101 * <li><b>positive</b> - in dst</li> 102 * <li><b>0</b> - not in dst</li> 103 * <li><b>negative</b> - unknown</li> 104 * </ul> 105 */ 106 public int isDst; 107 108 /** 109 * Offset from UTC (in seconds). 110 */ 111 public long gmtoff; 112 113 /** 114 * The timezone for this Time. Should not be null. 115 */ 116 public String timezone; 117 118 /* 119 * Define symbolic constants for accessing the fields in this class. Used in 120 * getActualMaximum(). 121 */ 122 public static final int SECOND = 1; 123 public static final int MINUTE = 2; 124 public static final int HOUR = 3; 125 public static final int MONTH_DAY = 4; 126 public static final int MONTH = 5; 127 public static final int YEAR = 6; 128 public static final int WEEK_DAY = 7; 129 public static final int YEAR_DAY = 8; 130 public static final int WEEK_NUM = 9; 131 132 public static final int SUNDAY = 0; 133 public static final int MONDAY = 1; 134 public static final int TUESDAY = 2; 135 public static final int WEDNESDAY = 3; 136 public static final int THURSDAY = 4; 137 public static final int FRIDAY = 5; 138 public static final int SATURDAY = 6; 139 140 /* 141 * The Locale for which date formatting strings have been loaded. 142 */ 143 private static Locale sLocale; 144 private static String[] sShortMonths; 145 private static String[] sLongMonths; 146 private static String[] sLongStandaloneMonths; 147 private static String[] sShortWeekdays; 148 private static String[] sLongWeekdays; 149 private static String sTimeOnlyFormat; 150 private static String sDateOnlyFormat; 151 private static String sDateTimeFormat; 152 private static String sAm; 153 private static String sPm; 154 private static char sZeroDigit; 155 156 // Referenced by native code. 157 private static String sDateCommand = "%a %b %e %H:%M:%S %Z %Y"; 158 159 /** 160 * Construct a Time object in the timezone named by the string 161 * argument "timezone". The time is initialized to Jan 1, 1970. 162 * @param timezone string containing the timezone to use. 163 * @see TimeZone 164 */ 165 public Time(String timezone) { 166 if (timezone == null) { 167 throw new NullPointerException("timezone is null!"); 168 } 169 this.timezone = timezone; 170 this.year = 1970; 171 this.monthDay = 1; 172 // Set the daylight-saving indicator to the unknown value -1 so that 173 // it will be recomputed. 174 this.isDst = -1; 175 } 176 177 /** 178 * Construct a Time object in the default timezone. The time is initialized to 179 * Jan 1, 1970. 180 */ 181 public Time() { 182 this(TimeZone.getDefault().getID()); 183 } 184 185 /** 186 * A copy constructor. Construct a Time object by copying the given 187 * Time object. No normalization occurs. 188 * 189 * @param other 190 */ 191 public Time(Time other) { 192 set(other); 193 } 194 195 /** 196 * Ensures the values in each field are in range. For example if the 197 * current value of this calendar is March 32, normalize() will convert it 198 * to April 1. It also fills in weekDay, yearDay, isDst and gmtoff. 199 * 200 * <p> 201 * If "ignoreDst" is true, then this method sets the "isDst" field to -1 202 * (the "unknown" value) before normalizing. It then computes the 203 * correct value for "isDst". 204 * 205 * <p> 206 * See {@link #toMillis(boolean)} for more information about when to 207 * use <tt>true</tt> or <tt>false</tt> for "ignoreDst". 208 * 209 * @return the UTC milliseconds since the epoch 210 */ 211 native public long normalize(boolean ignoreDst); 212 213 /** 214 * Convert this time object so the time represented remains the same, but is 215 * instead located in a different timezone. This method automatically calls 216 * normalize() in some cases 217 */ 218 native public void switchTimezone(String timezone); 219 220 private static final int[] DAYS_PER_MONTH = { 31, 28, 31, 30, 31, 30, 31, 221 31, 30, 31, 30, 31 }; 222 223 /** 224 * Return the maximum possible value for the given field given the value of 225 * the other fields. Requires that it be normalized for MONTH_DAY and 226 * YEAR_DAY. 227 * @param field one of the constants for HOUR, MINUTE, SECOND, etc. 228 * @return the maximum value for the field. 229 */ 230 public int getActualMaximum(int field) { 231 switch (field) { 232 case SECOND: 233 return 59; // leap seconds, bah humbug 234 case MINUTE: 235 return 59; 236 case HOUR: 237 return 23; 238 case MONTH_DAY: { 239 int n = DAYS_PER_MONTH[this.month]; 240 if (n != 28) { 241 return n; 242 } else { 243 int y = this.year; 244 return ((y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0)) ? 29 : 28; 245 } 246 } 247 case MONTH: 248 return 11; 249 case YEAR: 250 return 2037; 251 case WEEK_DAY: 252 return 6; 253 case YEAR_DAY: { 254 int y = this.year; 255 // Year days are numbered from 0, so the last one is usually 364. 256 return ((y % 4) == 0 && ((y % 100) != 0 || (y % 400) == 0)) ? 365 : 364; 257 } 258 case WEEK_NUM: 259 throw new RuntimeException("WEEK_NUM not implemented"); 260 default: 261 throw new RuntimeException("bad field=" + field); 262 } 263 } 264 265 /** 266 * Clears all values, setting the timezone to the given timezone. Sets isDst 267 * to a negative value to mean "unknown". 268 * @param timezone the timezone to use. 269 */ 270 public void clear(String timezone) { 271 if (timezone == null) { 272 throw new NullPointerException("timezone is null!"); 273 } 274 this.timezone = timezone; 275 this.allDay = false; 276 this.second = 0; 277 this.minute = 0; 278 this.hour = 0; 279 this.monthDay = 0; 280 this.month = 0; 281 this.year = 0; 282 this.weekDay = 0; 283 this.yearDay = 0; 284 this.gmtoff = 0; 285 this.isDst = -1; 286 } 287 288 /** 289 * Compare two {@code Time} objects and return a negative number if {@code 290 * a} is less than {@code b}, a positive number if {@code a} is greater than 291 * {@code b}, or 0 if they are equal. 292 * 293 * @param a first {@code Time} instance to compare 294 * @param b second {@code Time} instance to compare 295 * @throws NullPointerException if either argument is {@code null} 296 * @throws IllegalArgumentException if {@link #allDay} is true but {@code 297 * hour}, {@code minute}, and {@code second} are not 0. 298 * @return a negative result if {@code a} is earlier, a positive result if 299 * {@code a} is earlier, or 0 if they are equal. 300 */ 301 public static int compare(Time a, Time b) { 302 if (a == null) { 303 throw new NullPointerException("a == null"); 304 } else if (b == null) { 305 throw new NullPointerException("b == null"); 306 } 307 308 return nativeCompare(a, b); 309 } 310 311 private static native int nativeCompare(Time a, Time b); 312 313 /** 314 * Print the current value given the format string provided. See man 315 * strftime for what means what. The final string must be less than 256 316 * characters. 317 * @param format a string containing the desired format. 318 * @return a String containing the current time expressed in the current locale. 319 */ 320 public String format(String format) { 321 synchronized (Time.class) { 322 Locale locale = Locale.getDefault(); 323 324 if (sLocale == null || locale == null || !(locale.equals(sLocale))) { 325 LocaleData localeData = LocaleData.get(locale); 326 327 sAm = localeData.amPm[0]; 328 sPm = localeData.amPm[1]; 329 sZeroDigit = localeData.zeroDigit; 330 331 sShortMonths = localeData.shortMonthNames; 332 sLongMonths = localeData.longMonthNames; 333 sLongStandaloneMonths = localeData.longStandAloneMonthNames; 334 sShortWeekdays = localeData.shortWeekdayNames; 335 sLongWeekdays = localeData.longWeekdayNames; 336 337 Resources r = Resources.getSystem(); 338 sTimeOnlyFormat = r.getString(com.android.internal.R.string.time_of_day); 339 sDateOnlyFormat = r.getString(com.android.internal.R.string.month_day_year); 340 sDateTimeFormat = r.getString(com.android.internal.R.string.date_and_time); 341 342 sLocale = locale; 343 } 344 345 String result = format1(format); 346 if (sZeroDigit != '0') { 347 result = localizeDigits(result); 348 } 349 return result; 350 } 351 } 352 353 native private String format1(String format); 354 355 // TODO: unify this with java.util.Formatter's copy. 356 private String localizeDigits(String s) { 357 int length = s.length(); 358 int offsetToLocalizedDigits = sZeroDigit - '0'; 359 StringBuilder result = new StringBuilder(length); 360 for (int i = 0; i < length; ++i) { 361 char ch = s.charAt(i); 362 if (ch >= '0' && ch <= '9') { 363 ch += offsetToLocalizedDigits; 364 } 365 result.append(ch); 366 } 367 return result.toString(); 368 } 369 370 371 /** 372 * Return the current time in YYYYMMDDTHHMMSS<tz> format 373 */ 374 @Override 375 native public String toString(); 376 377 /** 378 * Parses a date-time string in either the RFC 2445 format or an abbreviated 379 * format that does not include the "time" field. For example, all of the 380 * following strings are valid: 381 * 382 * <ul> 383 * <li>"20081013T160000Z"</li> 384 * <li>"20081013T160000"</li> 385 * <li>"20081013"</li> 386 * </ul> 387 * 388 * Returns whether or not the time is in UTC (ends with Z). If the string 389 * ends with "Z" then the timezone is set to UTC. If the date-time string 390 * included only a date and no time field, then the <code>allDay</code> 391 * field of this Time class is set to true and the <code>hour</code>, 392 * <code>minute</code>, and <code>second</code> fields are set to zero; 393 * otherwise (a time field was included in the date-time string) 394 * <code>allDay</code> is set to false. The fields <code>weekDay</code>, 395 * <code>yearDay</code>, and <code>gmtoff</code> are always set to zero, 396 * and the field <code>isDst</code> is set to -1 (unknown). To set those 397 * fields, call {@link #normalize(boolean)} after parsing. 398 * 399 * To parse a date-time string and convert it to UTC milliseconds, do 400 * something like this: 401 * 402 * <pre> 403 * Time time = new Time(); 404 * String date = "20081013T160000Z"; 405 * time.parse(date); 406 * long millis = time.normalize(false); 407 * </pre> 408 * 409 * @param s the string to parse 410 * @return true if the resulting time value is in UTC time 411 * @throws android.util.TimeFormatException if s cannot be parsed. 412 */ 413 public boolean parse(String s) { 414 if (s == null) { 415 throw new NullPointerException("time string is null"); 416 } 417 if (nativeParse(s)) { 418 timezone = TIMEZONE_UTC; 419 return true; 420 } 421 return false; 422 } 423 424 /** 425 * Parse a time in the current zone in YYYYMMDDTHHMMSS format. 426 */ 427 native private boolean nativeParse(String s); 428 429 /** 430 * Parse a time in RFC 3339 format. This method also parses simple dates 431 * (that is, strings that contain no time or time offset). For example, 432 * all of the following strings are valid: 433 * 434 * <ul> 435 * <li>"2008-10-13T16:00:00.000Z"</li> 436 * <li>"2008-10-13T16:00:00.000+07:00"</li> 437 * <li>"2008-10-13T16:00:00.000-07:00"</li> 438 * <li>"2008-10-13"</li> 439 * </ul> 440 * 441 * <p> 442 * If the string contains a time and time offset, then the time offset will 443 * be used to convert the time value to UTC. 444 * </p> 445 * 446 * <p> 447 * If the given string contains just a date (with no time field), then 448 * the {@link #allDay} field is set to true and the {@link #hour}, 449 * {@link #minute}, and {@link #second} fields are set to zero. 450 * </p> 451 * 452 * <p> 453 * Returns true if the resulting time value is in UTC time. 454 * </p> 455 * 456 * @param s the string to parse 457 * @return true if the resulting time value is in UTC time 458 * @throws android.util.TimeFormatException if s cannot be parsed. 459 */ 460 public boolean parse3339(String s) { 461 if (s == null) { 462 throw new NullPointerException("time string is null"); 463 } 464 if (nativeParse3339(s)) { 465 timezone = TIMEZONE_UTC; 466 return true; 467 } 468 return false; 469 } 470 471 native private boolean nativeParse3339(String s); 472 473 /** 474 * Returns the timezone string that is currently set for the device. 475 */ 476 public static String getCurrentTimezone() { 477 return TimeZone.getDefault().getID(); 478 } 479 480 /** 481 * Sets the time of the given Time object to the current time. 482 */ 483 native public void setToNow(); 484 485 /** 486 * Converts this time to milliseconds. Suitable for interacting with the 487 * standard java libraries. The time is in UTC milliseconds since the epoch. 488 * This does an implicit normalization to compute the milliseconds but does 489 * <em>not</em> change any of the fields in this Time object. If you want 490 * to normalize the fields in this Time object and also get the milliseconds 491 * then use {@link #normalize(boolean)}. 492 * 493 * <p> 494 * If "ignoreDst" is false, then this method uses the current setting of the 495 * "isDst" field and will adjust the returned time if the "isDst" field is 496 * wrong for the given time. See the sample code below for an example of 497 * this. 498 * 499 * <p> 500 * If "ignoreDst" is true, then this method ignores the current setting of 501 * the "isDst" field in this Time object and will instead figure out the 502 * correct value of "isDst" (as best it can) from the fields in this 503 * Time object. The only case where this method cannot figure out the 504 * correct value of the "isDst" field is when the time is inherently 505 * ambiguous because it falls in the hour that is repeated when switching 506 * from Daylight-Saving Time to Standard Time. 507 * 508 * <p> 509 * Here is an example where <tt>toMillis(true)</tt> adjusts the time, 510 * assuming that DST changes at 2am on Sunday, Nov 4, 2007. 511 * 512 * <pre> 513 * Time time = new Time(); 514 * time.set(4, 10, 2007); // set the date to Nov 4, 2007, 12am 515 * time.normalize(); // this sets isDst = 1 516 * time.monthDay += 1; // changes the date to Nov 5, 2007, 12am 517 * millis = time.toMillis(false); // millis is Nov 4, 2007, 11pm 518 * millis = time.toMillis(true); // millis is Nov 5, 2007, 12am 519 * </pre> 520 * 521 * <p> 522 * To avoid this problem, use <tt>toMillis(true)</tt> 523 * after adding or subtracting days or explicitly setting the "monthDay" 524 * field. On the other hand, if you are adding 525 * or subtracting hours or minutes, then you should use 526 * <tt>toMillis(false)</tt>. 527 * 528 * <p> 529 * You should also use <tt>toMillis(false)</tt> if you want 530 * to read back the same milliseconds that you set with {@link #set(long)} 531 * or {@link #set(Time)} or after parsing a date string. 532 */ 533 native public long toMillis(boolean ignoreDst); 534 535 /** 536 * Sets the fields in this Time object given the UTC milliseconds. After 537 * this method returns, all the fields are normalized. 538 * This also sets the "isDst" field to the correct value. 539 * 540 * @param millis the time in UTC milliseconds since the epoch. 541 */ 542 native public void set(long millis); 543 544 /** 545 * Format according to RFC 2445 DATETIME type. 546 * 547 * <p> 548 * The same as format("%Y%m%dT%H%M%S"). 549 */ 550 native public String format2445(); 551 552 /** 553 * Copy the value of that to this Time object. No normalization happens. 554 */ 555 public void set(Time that) { 556 this.timezone = that.timezone; 557 this.allDay = that.allDay; 558 this.second = that.second; 559 this.minute = that.minute; 560 this.hour = that.hour; 561 this.monthDay = that.monthDay; 562 this.month = that.month; 563 this.year = that.year; 564 this.weekDay = that.weekDay; 565 this.yearDay = that.yearDay; 566 this.isDst = that.isDst; 567 this.gmtoff = that.gmtoff; 568 } 569 570 /** 571 * Sets the fields. Sets weekDay, yearDay and gmtoff to 0, and isDst to -1. 572 * Call {@link #normalize(boolean)} if you need those. 573 */ 574 public void set(int second, int minute, int hour, int monthDay, int month, int year) { 575 this.allDay = false; 576 this.second = second; 577 this.minute = minute; 578 this.hour = hour; 579 this.monthDay = monthDay; 580 this.month = month; 581 this.year = year; 582 this.weekDay = 0; 583 this.yearDay = 0; 584 this.isDst = -1; 585 this.gmtoff = 0; 586 } 587 588 /** 589 * Sets the date from the given fields. Also sets allDay to true. 590 * Sets weekDay, yearDay and gmtoff to 0, and isDst to -1. 591 * Call {@link #normalize(boolean)} if you need those. 592 * 593 * @param monthDay the day of the month (in the range [1,31]) 594 * @param month the zero-based month number (in the range [0,11]) 595 * @param year the year 596 */ 597 public void set(int monthDay, int month, int year) { 598 this.allDay = true; 599 this.second = 0; 600 this.minute = 0; 601 this.hour = 0; 602 this.monthDay = monthDay; 603 this.month = month; 604 this.year = year; 605 this.weekDay = 0; 606 this.yearDay = 0; 607 this.isDst = -1; 608 this.gmtoff = 0; 609 } 610 611 /** 612 * Returns true if the time represented by this Time object occurs before 613 * the given time. 614 * 615 * @param that a given Time object to compare against 616 * @return true if this time is less than the given time 617 */ 618 public boolean before(Time that) { 619 return Time.compare(this, that) < 0; 620 } 621 622 623 /** 624 * Returns true if the time represented by this Time object occurs after 625 * the given time. 626 * 627 * @param that a given Time object to compare against 628 * @return true if this time is greater than the given time 629 */ 630 public boolean after(Time that) { 631 return Time.compare(this, that) > 0; 632 } 633 634 /** 635 * This array is indexed by the weekDay field (SUNDAY=0, MONDAY=1, etc.) 636 * and gives a number that can be added to the yearDay to give the 637 * closest Thursday yearDay. 638 */ 639 private static final int[] sThursdayOffset = { -3, 3, 2, 1, 0, -1, -2 }; 640 641 /** 642 * Computes the week number according to ISO 8601. The current Time 643 * object must already be normalized because this method uses the 644 * yearDay and weekDay fields. 645 * 646 * <p> 647 * In IS0 8601, weeks start on Monday. 648 * The first week of the year (week 1) is defined by ISO 8601 as the 649 * first week with four or more of its days in the starting year. 650 * Or equivalently, the week containing January 4. Or equivalently, 651 * the week with the year's first Thursday in it. 652 * </p> 653 * 654 * <p> 655 * The week number can be calculated by counting Thursdays. Week N 656 * contains the Nth Thursday of the year. 657 * </p> 658 * 659 * @return the ISO week number. 660 */ 661 public int getWeekNumber() { 662 // Get the year day for the closest Thursday 663 int closestThursday = yearDay + sThursdayOffset[weekDay]; 664 665 // Year days start at 0 666 if (closestThursday >= 0 && closestThursday <= 364) { 667 return closestThursday / 7 + 1; 668 } 669 670 // The week crosses a year boundary. 671 Time temp = new Time(this); 672 temp.monthDay += sThursdayOffset[weekDay]; 673 temp.normalize(true /* ignore isDst */); 674 return temp.yearDay / 7 + 1; 675 } 676 677 /** 678 * Return a string in the RFC 3339 format. 679 * <p> 680 * If allDay is true, expresses the time as Y-M-D</p> 681 * <p> 682 * Otherwise, if the timezone is UTC, expresses the time as Y-M-D-T-H-M-S UTC</p> 683 * <p> 684 * Otherwise the time is expressed the time as Y-M-D-T-H-M-S +- GMT</p> 685 * @param allDay 686 * @return string in the RFC 3339 format. 687 */ 688 public String format3339(boolean allDay) { 689 if (allDay) { 690 return format(Y_M_D); 691 } else if (TIMEZONE_UTC.equals(timezone)) { 692 return format(Y_M_D_T_H_M_S_000_Z); 693 } else { 694 String base = format(Y_M_D_T_H_M_S_000); 695 String sign = (gmtoff < 0) ? "-" : "+"; 696 int offset = (int)Math.abs(gmtoff); 697 int minutes = (offset % 3600) / 60; 698 int hours = offset / 3600; 699 700 return String.format(Locale.US, "%s%s%02d:%02d", base, sign, hours, minutes); 701 } 702 } 703 704 /** 705 * Returns true if the day of the given time is the epoch on the Julian Calendar 706 * (January 1, 1970 on the Gregorian calendar). 707 * 708 * @param time the time to test 709 * @return true if epoch. 710 */ 711 public static boolean isEpoch(Time time) { 712 long millis = time.toMillis(true); 713 return getJulianDay(millis, 0) == EPOCH_JULIAN_DAY; 714 } 715 716 /** 717 * Computes the Julian day number, given the UTC milliseconds 718 * and the offset (in seconds) from UTC. The Julian day for a given 719 * date will be the same for every timezone. For example, the Julian 720 * day for July 1, 2008 is 2454649. This is the same value no matter 721 * what timezone is being used. The Julian day is useful for testing 722 * if two events occur on the same day and for determining the relative 723 * time of an event from the present ("yesterday", "3 days ago", etc.). 724 * 725 * <p> 726 * Use {@link #toMillis(boolean)} to get the milliseconds. 727 * 728 * @param millis the time in UTC milliseconds 729 * @param gmtoff the offset from UTC in seconds 730 * @return the Julian day 731 */ 732 public static int getJulianDay(long millis, long gmtoff) { 733 long offsetMillis = gmtoff * 1000; 734 long julianDay = (millis + offsetMillis) / DateUtils.DAY_IN_MILLIS; 735 return (int) julianDay + EPOCH_JULIAN_DAY; 736 } 737 738 /** 739 * <p>Sets the time from the given Julian day number, which must be based on 740 * the same timezone that is set in this Time object. The "gmtoff" field 741 * need not be initialized because the given Julian day may have a different 742 * GMT offset than whatever is currently stored in this Time object anyway. 743 * After this method returns all the fields will be normalized and the time 744 * will be set to 12am at the beginning of the given Julian day. 745 * </p> 746 * 747 * <p> 748 * The only exception to this is if 12am does not exist for that day because 749 * of daylight saving time. For example, Cairo, Eqypt moves time ahead one 750 * hour at 12am on April 25, 2008 and there are a few other places that 751 * also change daylight saving time at 12am. In those cases, the time 752 * will be set to 1am. 753 * </p> 754 * 755 * @param julianDay the Julian day in the timezone for this Time object 756 * @return the UTC milliseconds for the beginning of the Julian day 757 */ 758 public long setJulianDay(int julianDay) { 759 // Don't bother with the GMT offset since we don't know the correct 760 // value for the given Julian day. Just get close and then adjust 761 // the day. 762 long millis = (julianDay - EPOCH_JULIAN_DAY) * DateUtils.DAY_IN_MILLIS; 763 set(millis); 764 765 // Figure out how close we are to the requested Julian day. 766 // We can't be off by more than a day. 767 int approximateDay = getJulianDay(millis, gmtoff); 768 int diff = julianDay - approximateDay; 769 monthDay += diff; 770 771 // Set the time to 12am and re-normalize. 772 hour = 0; 773 minute = 0; 774 second = 0; 775 millis = normalize(true); 776 return millis; 777 } 778 779 /** 780 * Returns the week since {@link #EPOCH_JULIAN_DAY} (Jan 1, 1970) adjusted 781 * for first day of week. This takes a julian day and the week start day and 782 * calculates which week since {@link #EPOCH_JULIAN_DAY} that day occurs in, 783 * starting at 0. *Do not* use this to compute the ISO week number for the 784 * year. 785 * 786 * @param julianDay The julian day to calculate the week number for 787 * @param firstDayOfWeek Which week day is the first day of the week, see 788 * {@link #SUNDAY} 789 * @return Weeks since the epoch 790 */ 791 public static int getWeeksSinceEpochFromJulianDay(int julianDay, int firstDayOfWeek) { 792 int diff = THURSDAY - firstDayOfWeek; 793 if (diff < 0) { 794 diff += 7; 795 } 796 int refDay = EPOCH_JULIAN_DAY - diff; 797 return (julianDay - refDay) / 7; 798 } 799 800 /** 801 * Takes a number of weeks since the epoch and calculates the Julian day of 802 * the Monday for that week. This assumes that the week containing the 803 * {@link #EPOCH_JULIAN_DAY} is considered week 0. It returns the Julian day 804 * for the Monday week weeks after the Monday of the week containing the 805 * epoch. 806 * 807 * @param week Number of weeks since the epoch 808 * @return The julian day for the Monday of the given week since the epoch 809 */ 810 public static int getJulianMondayFromWeeksSinceEpoch(int week) { 811 return MONDAY_BEFORE_JULIAN_EPOCH + week * 7; 812 } 813} 814