1 2(********************************************************************) 3(* *) 4(* time.s7i Time and date library *) 5(* Copyright (C) 1991 - 1994, 2005 - 2013 Thomas Mertes *) 6(* *) 7(* This file is part of the Seed7 Runtime Library. *) 8(* *) 9(* The Seed7 Runtime Library is free software; you can *) 10(* redistribute it and/or modify it under the terms of the GNU *) 11(* Lesser General Public License as published by the Free Software *) 12(* Foundation; either version 2.1 of the License, or (at your *) 13(* option) any later version. *) 14(* *) 15(* The Seed7 Runtime Library is distributed in the hope that it *) 16(* will be useful, but WITHOUT ANY WARRANTY; without even the *) 17(* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR *) 18(* PURPOSE. See the GNU Lesser General Public License for more *) 19(* details. *) 20(* *) 21(* You should have received a copy of the GNU Lesser General *) 22(* Public License along with this program; if not, write to the *) 23(* Free Software Foundation, Inc., 51 Franklin Street, *) 24(* Fifth Floor, Boston, MA 02110-1301, USA. *) 25(* *) 26(********************************************************************) 27 28 29include "enable_io.s7i"; 30 31 32(** 33 * Describes times and dates. 34 * For dates the proleptic Gregorian calendar is used (which assumes 35 * that the Gregorian calendar was even in effect at dates preceding 36 * its official introduction). This convention is used according to 37 * ISO 8601 which also defines that positive and negative years exist 38 * and that the year preceding 1 is 0. Time is measured in hours, 39 * minutes, seconds and micro seconds. Additionally information about 40 * the difference to UTC and a flag indicating daylight saving time 41 * is maintained also. 42 *) 43const type: time is new object struct 44 var integer: year is 0; 45 var integer: month is 1; 46 var integer: day is 1; 47 var integer: hour is 0; 48 var integer: minute is 0; 49 var integer: second is 0; 50 var integer: micro_second is 0; 51 var integer: timeZone is 0; 52 var boolean: daylightSavingTime is FALSE; 53 end struct; 54 55 56const proc: GET_TIME (inout integer: year, inout integer: month, inout integer: day, 57 inout integer: hour, inout integer: minute, inout integer: second, 58 inout integer: micro_second, inout integer: timeZone, 59 inout boolean: daylightSavingTime) is action "TIM_NOW"; 60 61const proc: AWAIT_TIME (in integer: year, in integer: month, in integer: day, 62 in integer: hour, in integer: minute, in integer: second, 63 in integer: micro_second, in integer: timeZone) is action "TIM_AWAIT"; 64 65const proc: FROM_TIMESTAMP (in integer: timestamp, inout integer: year, 66 inout integer: month, inout integer: day, inout integer: hour, 67 inout integer: minute, inout integer: second, inout integer: micro_second, 68 inout integer: timeZone, inout boolean: daylightSavingTime) is action "TIM_FROM_TIMESTAMP"; 69 70const proc: SET_LOCAL_TZ (in integer: year, in integer: month, in integer: day, 71 in integer: hour, in integer: minute, in integer: second, 72 inout integer: timeZone, inout boolean: daylightSavingTime) is action "TIM_SET_LOCAL_TZ"; 73 74 75(** 76 * Determine if a year is a leap year in the Gregorian calendar. 77 * @return TRUE if the year is a leap year, FALSE otherwise. 78 *) 79const func boolean: isLeapYear (in integer: year) is 80 return (year rem 4 = 0 and year rem 100 <> 0) or year rem 400 = 0; 81 82 83(** 84 * Determine the number of days in a 'year'. 85 * @return the number of days in the year. 86 *) 87const func integer: daysInYear (in integer: year) is func 88 result 89 var integer: days is 0; 90 begin 91 if isLeapYear(year) then 92 days := 366; 93 else 94 days := 365; 95 end if; 96 end func; 97 98 99(** 100 * Determine the number of days in the 'month' of the given 'year'. 101 * @return the number of days in the month. 102 *) 103const func integer: daysInMonth (in integer: year, in integer: month) is func 104 result 105 var integer: leng is 0; 106 local 107 const set of integer: monthsOfLength31 is {1, 3, 5, 7, 8, 10, 12}; 108 begin 109 if month in monthsOfLength31 then 110 leng := 31; 111 else 112 if month = 2 then 113 if isLeapYear(year) then 114 leng := 29; 115 else 116 leng := 28; 117 end if; 118 else 119 leng := 30; 120 end if; 121 end if; 122 end func; 123 124 125(** 126 * Determine the number of days in the month of a given date. 127 * @return the number of days in the month. 128 *) 129const func integer: daysInMonth (in time: date) is 130 return daysInMonth(date.year, date.month); 131 132 133(** 134 * Convert a time to a string with ISO 8601 YYYY-MM-DD date format. 135 * @return the string result of the conversion. 136 *) 137const func string: strDate (in time: aTime) is 138 return aTime.year lpad0 4 <& "-" <& 139 aTime.month lpad0 2 <& "-" <& 140 aTime.day lpad0 2; 141 142 143(** 144 * Convert a time to a string with ISO 8601 hh:mm:ss time format. 145 * @return the string result of the conversion. 146 *) 147const func string: strTime (in time: aTime) is func 148 result 149 var string: isoTime is ""; 150 begin 151 isoTime := aTime.hour lpad0 2 <& ":" <& 152 aTime.minute lpad0 2 <& ":" <& 153 aTime.second lpad0 2; 154 if aTime.micro_second <> 0 then 155 isoTime &:= "." <& aTime.micro_second lpad0 6; 156 end if; 157 end func; 158 159 160const func string: strTimeZone (in time: aTime) is func 161 result 162 var string: timeZone is "UTC"; 163 begin 164 if aTime.timeZone <> 0 then 165 if aTime.timeZone > 0 then 166 timeZone &:= "+"; 167 end if; 168 timeZone &:= str(aTime.timeZone div 60); 169 if aTime.timeZone rem 60 <> 0 then 170 timeZone &:= ":" & str(abs(aTime.timeZone) rem 60); 171 end if; 172 end if; 173 if aTime.daylightSavingTime then 174 timeZone &:= " (DST)"; 175 end if; 176 end func; 177 178 179const func string: str_yyyy_mm_dd (in time: aTime, in string: delimiter) is 180 return aTime.year lpad0 4 <& delimiter <& 181 aTime.month lpad0 2 <& delimiter <& 182 aTime.day lpad0 2; 183 184 185const func string: str_yy_mm_dd (in time: aTime, in string: delimiter) is 186 return aTime.year rem 100 lpad0 2 <& delimiter <& 187 aTime.month lpad0 2 <& delimiter <& 188 aTime.day lpad0 2; 189 190 191const func string: str_mm_dd_yyyy (in time: aTime, in string: delimiter) is 192 return aTime.month lpad0 2 <& delimiter <& 193 aTime.day lpad0 2 <& delimiter <& 194 aTime.year lpad0 4; 195 196 197const func string: str_mm_dd_yy (in time: aTime, in string: delimiter) is 198 return aTime.month lpad0 2 <& delimiter <& 199 aTime.day lpad0 2 <& delimiter <& 200 aTime.year rem 100 lpad0 2; 201 202 203const func string: str_dd_mm_yyyy (in time: aTime, in string: delimiter) is 204 return aTime.day lpad0 2 <& delimiter <& 205 aTime.month lpad0 2 <& delimiter <& 206 aTime.year lpad0 4; 207 208 209const func string: str_dd_mm_yy (in time: aTime, in string: delimiter) is 210 return aTime.day lpad0 2 <& delimiter <& 211 aTime.month lpad0 2 <& delimiter <& 212 aTime.year rem 100 lpad0 2; 213 214 215const func string: str_d_m_yyyy (in time: aTime, in string: delimiter) is 216 return str(aTime.day) <& delimiter <& 217 str(aTime.month) <& delimiter <& 218 aTime.year lpad0 4; 219 220 221const func string: str_d_m_yy (in time: aTime, in string: delimiter) is 222 return str(aTime.day) <& delimiter <& 223 str(aTime.month) <& delimiter <& 224 aTime.year rem 100 lpad0 2; 225 226 227const func string: str_hh_mm (in time: aTime, in string: delimiter) is 228 return aTime.hour lpad0 2 <& delimiter <& 229 aTime.minute lpad0 2; 230 231 232const func string: str_hh_mm_ss (in time: aTime, in string: delimiter) is 233 return aTime.hour lpad0 2 <& delimiter <& 234 aTime.minute lpad0 2 <& delimiter <& 235 aTime.second lpad0 2; 236 237 238(** 239 * Convert a time to a string with ISO 8601 date and time format. 240 * The time is converted to the YYYY-MM-DD hh:mm:ss format. 241 * Microseconds, time zone and information about the daylight 242 * saving time are omitted. 243 * @return the string result of the conversion. 244 *) 245const func string: strDateTime (in time: aTime) is 246 return aTime.year lpad0 4 <& "-" <& 247 aTime.month lpad0 2 <& "-" <& 248 aTime.day lpad0 2 <& " " <& 249 aTime.hour lpad0 2 <& ":" <& 250 aTime.minute lpad0 2 <& ":" <& 251 aTime.second lpad0 2; 252 253 254(** 255 * Convert a time to a string with ISO 8601 date and time format. 256 * The string has the format YYYY-MM-DD hh:mm:ss.uuuuuu followed by a 257 * time zone in the format UTC+n and (DST), if it is a daylight 258 * saving time. The microseconds (uuuuuu) are omitted, if they are 259 * zero. A time zone of UTC+0 is also omitted. 260 * @return the string result of the conversion. 261 *) 262const func string: str (in time: aTime) is 263 return strDate(aTime) <& " " <& 264 strTime(aTime) <& " " <& 265 strTimeZone(aTime); 266 267 268(** 269 * Convert a time to a time literal. 270 * @return the time literal. 271 *) 272const func string: literal (in time: aTime) is 273 return "time(" & literal(str(aTime)) & ")"; 274 275 276(** 277 * Convert a string in the ISO 8601 date format to a time. 278 * The accepted ISO 8601 date formats are YYYY-MM, YYYY-MM-DD, 279 * YYYY-MM-DDTHH, YYYY-MM-DDTHH:MM, YYYY-MM-DDTHH:MM:SS 280 * Additionally a space is also allowed to separate the date and the 281 * time representations: YYYY-MM-DD HH, YYYY-MM-DD HH:MM, 282 * YYYY-MM-DD HH:MM:SS 283 * @return the time result of the conversion. 284 * @exception RANGE_ERROR If stri contains not a valid time value. 285 *) 286const func time: time (in var string: stri) is func 287 result 288 var time: aTime is time.value; 289 local 290 var boolean: checkForTimeZone is FALSE; 291 begin 292 if stri[1] = '-' then 293 stri := stri[2 ..]; 294 aTime.year := -integer(getint(stri)); 295 else 296 aTime.year := integer(getint(stri)); 297 end if; 298 if stri <> "" and stri[1] = '-' then 299 stri := stri[2 ..]; 300 aTime.month := integer(getint(stri)); 301 if stri <> "" and stri[1] = '-' then 302 stri := stri[2 ..]; 303 aTime.day := integer(getint(stri)); 304 if stri <> "" and (stri[1] = ' ' or stri[1] = 'T') then 305 stri := stri[2 ..]; 306 aTime.hour := integer(getint(stri)); 307 if stri <> "" and stri[1] = ':' then 308 stri := stri[2 ..]; 309 aTime.minute := integer(getint(stri)); 310 if stri <> "" and stri[1] = ':' then 311 stri := stri[2 ..]; 312 aTime.second := integer(getint(stri)); 313 if stri <> "" and stri[1] = '.' then 314 stri := stri[2 ..]; 315 aTime.micro_second := integer((getint(stri) & "00000X")[.. 6]); 316 end if; 317 end if; 318 end if; 319 checkForTimeZone := TRUE; 320 end if; 321 end if; 322 elsif stri <> "" and stri[1] = ':' then 323 stri := stri[2 ..]; 324 aTime.hour := aTime.year; 325 aTime.year := 0; 326 aTime.minute := integer(getint(stri)); 327 if stri <> "" and stri[1] = ':' then 328 stri := stri[2 ..]; 329 aTime.second := integer(getint(stri)); 330 if stri <> "" and stri[1] = '.' then 331 stri := stri[2 ..]; 332 aTime.micro_second := integer((getint(stri) & "00000X")[.. 6]); 333 end if; 334 end if; 335 checkForTimeZone := TRUE; 336 end if; 337 if checkForTimeZone then 338 if startsWith(stri, " UTC") then 339 stri := stri[5 ..]; 340 end if; 341 if stri <> "" then 342 if stri[1] = '+' then 343 stri := stri[2 ..]; 344 aTime.timeZone := integer(getint(stri)) * 60; 345 if stri <> "" and stri[1] = ':' then 346 stri := stri[2 ..]; 347 aTime.timeZone +:= integer(getint(stri)); 348 end if; 349 elsif stri[1] = '-' then 350 stri := stri[2 ..]; 351 aTime.timeZone := -integer(getint(stri)) * 60; 352 if stri <> "" and stri[1] = ':' then 353 stri := stri[2 ..]; 354 aTime.timeZone -:= integer(getint(stri)); 355 end if; 356 end if; 357 end if; 358 end if; 359 if stri <> "" then 360 raise RANGE_ERROR; 361 end if; 362 aTime.daylightSavingTime := FALSE; 363 if aTime.month < 1 or aTime.month > 12 or 364 aTime.day < 1 or aTime.day > daysInMonth(aTime.year, aTime.month) or 365 aTime.hour < 0 or aTime.hour > 23 or 366 aTime.minute < 0 or aTime.minute > 59 or 367 aTime.second < 0 or aTime.second > 59 then 368 raise RANGE_ERROR; 369 end if; 370 end func; 371 372 373(** 374 * Convert a string in the ISO 8601 date format to a time. 375 * The accepted ISO 8601 date formats are YYYY-MM, YYYY-MM-DD, 376 * YYYY-MM-DDTHH, YYYY-MM-DDTHH:MM, YYYY-MM-DDTHH:MM:SS 377 * Additionally a space is also allowed to separate the date and the 378 * time representations: YYYY-MM-DD HH, YYYY-MM-DD HH:MM, 379 * YYYY-MM-DD HH:MM:SS 380 * @return the time result of the conversion. 381 * @exception RANGE_ERROR If stri contains not a valid time value. 382 *) 383const func time: (attr time) parse (in string: stri) is 384 return time(stri); 385 386 387enable_io(time); 388 389 390(** 391 * Check if two ''time'' values are equal. 392 * @return TRUE if both times are equal, FALSE otherwise. 393 *) 394const func boolean: (in time: aTime1) = (in time: aTime2) is 395 return 396 aTime1.year = aTime2.year and 397 aTime1.month = aTime2.month and 398 aTime1.day = aTime2.day and 399 aTime1.hour = aTime2.hour and 400 aTime1.minute = aTime2.minute and 401 aTime1.second = aTime2.second and 402 aTime1.micro_second = aTime2.micro_second; 403 404 405(** 406 * Check if two ''time'' values are not equal. 407 * @return FALSE if both times are equal, TRUE otherwise. 408 *) 409const func boolean: (in time: aTime1) <> (in time: aTime2) is 410 return 411 aTime1.year <> aTime2.year or 412 aTime1.month <> aTime2.month or 413 aTime1.day <> aTime2.day or 414 aTime1.hour <> aTime2.hour or 415 aTime1.minute <> aTime2.minute or 416 aTime1.second <> aTime2.second or 417 aTime1.micro_second <> aTime2.micro_second; 418 419 420(** 421 * Check if ''aTime1'' is less than or equal to ''aTime2''. 422 * @return TRUE if ''aTime1'' is less than or equal to ''aTime2'', 423 * FALSE otherwise. 424 *) 425const func boolean: (in time: aTime1) <= (in time: aTime2) is func 426 result 427 var boolean: isLessEqual is FALSE; 428 begin 429 if aTime1.year < aTime2.year then 430 isLessEqual := TRUE; 431 elsif aTime1.year = aTime2.year then 432 if aTime1.month < aTime2.month then 433 isLessEqual := TRUE; 434 elsif aTime1.month = aTime2.month then 435 if aTime1.day < aTime2.day then 436 isLessEqual := TRUE; 437 elsif aTime1.day = aTime2.day then 438 if aTime1.hour < aTime2.hour then 439 isLessEqual := TRUE; 440 elsif aTime1.hour = aTime2.hour then 441 if aTime1.minute < aTime2.minute then 442 isLessEqual := TRUE; 443 elsif aTime1.minute = aTime2.minute then 444 if aTime1.second < aTime2.second then 445 isLessEqual := TRUE; 446 elsif aTime1.second = aTime2.second then 447 if aTime1.micro_second <= aTime2.micro_second then 448 isLessEqual := TRUE; 449 end if; 450 end if; 451 end if; 452 end if; 453 end if; 454 end if; 455 end if; 456 end func; 457 458 459(** 460 * Check if ''aTime1'' is greater than or equal to ''aTime2''. 461 * @return TRUE if ''aTime1'' is greater than or equal to ''aTime2'', 462 * FALSE otherwise. 463 *) 464const func boolean: (in time: aTime1) >= (in time: aTime2) is func 465 result 466 var boolean: isGreaterEqual is FALSE; 467 begin 468 if aTime1.year > aTime2.year then 469 isGreaterEqual := TRUE; 470 elsif aTime1.year = aTime2.year then 471 if aTime1.month > aTime2.month then 472 isGreaterEqual := TRUE; 473 elsif aTime1.month = aTime2.month then 474 if aTime1.day > aTime2.day then 475 isGreaterEqual := TRUE; 476 elsif aTime1.day = aTime2.day then 477 if aTime1.hour > aTime2.hour then 478 isGreaterEqual := TRUE; 479 elsif aTime1.hour = aTime2.hour then 480 if aTime1.minute > aTime2.minute then 481 isGreaterEqual := TRUE; 482 elsif aTime1.minute = aTime2.minute then 483 if aTime1.second > aTime2.second then 484 isGreaterEqual := TRUE; 485 elsif aTime1.second = aTime2.second then 486 if aTime1.micro_second >= aTime2.micro_second then 487 isGreaterEqual := TRUE; 488 end if; 489 end if; 490 end if; 491 end if; 492 end if; 493 end if; 494 end if; 495 end func; 496 497 498(** 499 * Check if ''aTime1'' is less than ''aTime2''. 500 * @return TRUE if ''aTime1'' is less than ''aTime2'', 501 * FALSE otherwise. 502 *) 503const func boolean: (in time: aTime1) < (in time: aTime2) is 504 return not aTime1 >= aTime2; 505 506 507(** 508 * Check if ''aTime1'' is greater than ''aTime2''. 509 * @return TRUE if ''aTime1'' is greater than ''aTime2'', 510 * FALSE otherwise. 511 *) 512const func boolean: (in time: aTime1) > (in time: aTime2) is 513 return not aTime1 <= aTime2; 514 515 516(** 517 * Compares two times. 518 * @return -1, 0 or 1 if the first argument is considered to be 519 * respectively less than, equal to, or greater than the 520 * second. 521 *) 522const func integer: compare (in time: aTime1, in time: aTime2) is func 523 result 524 var integer: signumValue is 0; 525 begin 526 if aTime1.year < aTime2.year then 527 signumValue := -1; 528 elsif aTime1.year > aTime2.year then 529 signumValue := 1; 530 elsif aTime1.month < aTime2.month then 531 signumValue := -1; 532 elsif aTime1.month > aTime2.month then 533 signumValue := 1; 534 elsif aTime1.day < aTime2.day then 535 signumValue := -1; 536 elsif aTime1.day > aTime2.day then 537 signumValue := 1; 538 elsif aTime1.hour < aTime2.hour then 539 signumValue := -1; 540 elsif aTime1.hour > aTime2.hour then 541 signumValue := 1; 542 elsif aTime1.minute < aTime2.minute then 543 signumValue := -1; 544 elsif aTime1.minute > aTime2.minute then 545 signumValue := 1; 546 elsif aTime1.second < aTime2.second then 547 signumValue := -1; 548 elsif aTime1.second > aTime2.second then 549 signumValue := 1; 550 elsif aTime1.micro_second < aTime2.micro_second then 551 signumValue := -1; 552 elsif aTime1.micro_second > aTime2.micro_second then 553 signumValue := 1; 554 end if; 555 end func; 556 557 558(** 559 * Compute the hash value of a time. 560 * @return the hash value. 561 *) 562const func integer: hashCode (in time: aTime) is 563 return aTime.year << 6 + aTime.month << 5 + aTime.day << 4 + 564 aTime.hour << 3 + aTime.minute << 2 + aTime.second << 1 + 565 aTime.micro_second; 566 567 568(** 569 * Truncate 'aTime' to the beginning of a second. 570 * @return the truncated time. 571 *) 572const func time: truncToSecond (in time: aTime) is func 573 result 574 var time: truncatedTime is time.value; 575 begin 576 truncatedTime := aTime; 577 truncatedTime.micro_second := 0; 578 end func; 579 580 581(** 582 * Truncate 'aTime' to the beginning of a minute. 583 * @return the truncated time. 584 *) 585const func time: truncToMinute (in time: aTime) is func 586 result 587 var time: truncatedTime is time.value; 588 begin 589 truncatedTime := aTime; 590 truncatedTime.second := 0; 591 truncatedTime.micro_second := 0; 592 end func; 593 594 595(** 596 * Truncate 'aTime' to the beginning of a hour. 597 * @return the truncated time. 598 *) 599const func time: truncToHour (in time: aTime) is func 600 result 601 var time: truncatedTime is time.value; 602 begin 603 truncatedTime := aTime; 604 truncatedTime.minute := 0; 605 truncatedTime.second := 0; 606 truncatedTime.micro_second := 0; 607 end func; 608 609 610(** 611 * Truncate 'aTime' to the beginning of a day. 612 * @return the truncated time. 613 *) 614const func time: truncToDay (in time: aTime) is func 615 result 616 var time: truncatedTime is time.value; 617 begin 618 truncatedTime := aTime; 619 truncatedTime.hour := 0; 620 truncatedTime.minute := 0; 621 truncatedTime.second := 0; 622 truncatedTime.micro_second := 0; 623 end func; 624 625 626(** 627 * Truncate 'aTime' to the beginning of the first day in the month. 628 * @return the truncated time. 629 *) 630const func time: truncToMonth (in time: aTime) is func 631 result 632 var time: truncatedTime is time.value; 633 begin 634 truncatedTime := aTime; 635 truncatedTime.day := 1; 636 truncatedTime.hour := 0; 637 truncatedTime.minute := 0; 638 truncatedTime.second := 0; 639 truncatedTime.micro_second := 0; 640 end func; 641 642 643(** 644 * Truncate 'aTime' to the beginning of the first day in the year. 645 * @return the truncated time. 646 *) 647const func time: truncToYear (in time: aTime) is func 648 result 649 var time: truncatedTime is time.value; 650 begin 651 truncatedTime := aTime; 652 truncatedTime.month := 1; 653 truncatedTime.day := 1; 654 truncatedTime.hour := 0; 655 truncatedTime.minute := 0; 656 truncatedTime.second := 0; 657 truncatedTime.micro_second := 0; 658 end func; 659 660 661(** 662 * Return the weekday number of 'aDate'. 663 * @return 1 for monday, 2 for tuesday, and so on up to 7 for sunday. 664 *) 665const func integer: dayOfWeek (in time: aDate) is func 666 result 667 var integer: weekday is 0; 668 local 669 var integer: year is 0; 670 var integer: month is 0; 671 begin 672 year := aDate.year; 673 month := aDate.month; 674 if month <= 2 then 675 decr(year); 676 month +:= 12; 677 end if; 678 weekday := succ(pred(year + year mdiv 4 - year mdiv 100 + year mdiv 400 + 679 (31 * (month - 2)) mdiv 12 + aDate.day) mod 7); 680 end func; 681 682 683(** 684 * Return the day number in the year of 'aDate'. 685 * @return 1 for the 1. of january and successive values up to 366. 686 *) 687const func integer: dayOfYear (in time: aDate) is func 688 result 689 var integer: dayOfYear is 0; 690 begin 691 if isLeapYear(aDate.year) then 692 dayOfYear := [](0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335)[aDate.month] + aDate.day; 693 else 694 dayOfYear := [](0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334)[aDate.month] + aDate.day; 695 end if; 696 end func; 697 698 699(** 700 * Return the week number of the 'year' for the 'dayOfYear'. 701 * According to ISO 8601: Week number 1 of every year contains the 702 * 4. of january. Since 1st to 3rd of january might be in the 703 * previous week there can be also a week number 0. 704 * @return a week number from 0 to 53 for weeks belonging to the year 705 * of the given date. 706 *) 707const func integer: weekOfYear (in var integer: year, in integer: dayOfYear) is func 708 result 709 var integer: weekNumber is 0; 710 local 711 var integer: weekDayOfJan4 is 0; 712 begin 713 year := pred(year); 714 weekDayOfJan4 := succ(pred(year + year mdiv 4 - year mdiv 100 + year mdiv 400 + 32) mod 7); 715 weekNumber := (dayOfYear + weekDayOfJan4 - 5) mdiv 7 + 1; 716 end func; 717 718 719(** 720 * Return the week number of the year for 'aDate'. 721 * According to ISO 8601: Week number 1 of every year contains the 722 * 4. of january. Since 1st to 3rd of january might be in the 723 * previous week there can be also a week number 0. 724 * @return a week number from 0 to 53 for weeks belonging to the year 725 * of the given date. 726 *) 727const func integer: weekOfYear (in time: aDate) is 728 return weekOfYear(aDate.year, dayOfYear(aDate)); 729 730 731(** 732 * Return the year of the ISO 8601 week date for 'aDate'. 733 * At the beginning and the end of an Gregorian calendar year there 734 * might be days which belong to a week of the previous or next year. 735 * For example 2005-01-01 is 2004-W53-6 and 2007-12-31 is 2008-W01-1. 736 * @return the year in the range 'pred(aDate.year)' to 737 * 'succ(aDate.year)'. 738 *) 739const func integer: weekDateYear (in time: aDate) is func 740 result 741 var integer: weekDateYear is 0; 742 local 743 var integer: weekNumber is 0; 744 begin 745 weekNumber := weekOfYear(aDate.year, dayOfYear(aDate)); 746 if weekNumber <= 0 then 747 weekDateYear := pred(aDate.year); 748 elsif weekNumber >= 53 and aDate.day >= 29 and 749 weekOfYear(succ(aDate.year), 31 - aDate.day) = 1 then 750 weekDateYear := succ(aDate.year); 751 else 752 weekDateYear := aDate.year; 753 end if; 754 end func; 755 756 757(** 758 * Return the week number of the ISO 8601 week date for 'aDate'. 759 * At the beginning and the end of an Gregorian calendar year there 760 * might be days which belong to a week of the previous or next year. 761 * For example 2005-01-01 is 2004-W53-6 and 2007-12-31 is 2008-W01-1. 762 * @return the week number in the range 1 to 53. 763 *) 764const func integer: weekDateWeek (in time: aDate) is func 765 result 766 var integer: weekNumber is 0; 767 begin 768 weekNumber := weekOfYear(aDate.year, dayOfYear(aDate)); 769 if weekNumber <= 0 then 770 weekNumber := weekOfYear(pred(aDate.year), 366); 771 elsif weekNumber >= 53 and aDate.day >= 29 and 772 weekOfYear(succ(aDate.year), 31 - aDate.day) = 1 then 773 weekNumber := 1; 774 end if; 775 end func; 776 777 778const proc: NORMALIZE (inout time: tim) is func 779 local 780 var integer: month_length is 0; 781 begin 782 tim.second := tim.second + tim.micro_second mdiv 1000000; 783 tim.micro_second := tim.micro_second mod 1000000; 784 tim.minute := tim.minute + tim.second mdiv 60; 785 tim.second := tim.second mod 60; 786 tim.hour := tim.hour + tim.minute mdiv 60; 787 tim.minute := tim.minute mod 60; 788 tim.day := tim.day + tim.hour mdiv 24; 789 tim.hour := tim.hour mod 24; 790 tim.year := tim.year + pred(tim.month) mdiv 12; 791 tim.month := succ(pred(tim.month) mod 12); 792 month_length := daysInMonth(tim.year, tim.month); 793 while tim.day > month_length do 794 tim.day := tim.day - month_length; 795 if tim.month < 12 then 796 incr(tim.month); 797 else 798 tim.month := 1; 799 incr(tim.year); 800 end if; 801 month_length := daysInMonth(tim.year, tim.month); 802 end while; 803 while tim.day < 1 do 804 if tim.month > 1 then 805 decr(tim.month); 806 else 807 tim.month := 12; 808 decr(tim.year); 809 end if; 810 tim.day := tim.day + daysInMonth(tim.year, tim.month); 811 end while; 812 end func; 813 814 815(** 816 * Convert a time to Coordinated Universal Time (UTC). 817 * @return the time in UTC. 818 *) 819const func time: toUTC (in time: aTime) is func 820 result 821 var time: timeInUTC is time.value; 822 begin 823 timeInUTC := aTime; 824 timeInUTC.minute -:= aTime.timeZone; 825 timeInUTC.timeZone := 0; 826 NORMALIZE(timeInUTC); 827 end func; 828 829 830(** 831 * Sets timeZone and daylightSavingTime for a given time. 832 * @return the time in the local time zone. 833 *) 834const func time: setLocalTZ (in time: aTime) is func 835 result 836 var time: localTime is time.value; 837 begin 838 localTime := aTime; 839 SET_LOCAL_TZ(localTime.year, localTime.month, localTime.day, 840 localTime.hour, localTime.minute, localTime.second, 841 localTime.timeZone, localTime.daylightSavingTime); 842 end func; 843 844 845(** 846 * Compute the julian day number of 'aDate'. 847 * The julian day number is the number of days that have elapsed 848 * since January 1, 4713 BC in the proleptic Julian calendar. 849 * @return the julian day number. 850 *) 851const func integer: julianDayNumber (in time: aDate) is func 852 result 853 var integer: julianDayNumber is 0; 854 local 855 var integer: year is 0; 856 var integer: month is 0; 857 begin 858 year := aDate.year; 859 month := aDate.month; 860 if month <= 2 then 861 decr(year); 862 month +:= 12; 863 end if; 864 julianDayNumber := (1461 * (year + 4800)) mdiv 4 + 865 (367 * (month - 2)) mdiv 12 - 866 (3 * ((year + 4900) mdiv 100)) mdiv 4 + 867 aDate.day - 32075; 868 end func; 869 870 871(** 872 * Return the time of a 'julianDayNumber'. 873 * The julian day number is the number of days that have elapsed 874 * since January 1, 4713 BC in the proleptic Julian calendar. 875 * @return the time 0:00:00 at the day with the 'julianDayNumber'. 876 *) 877const func time: julianDayNumToTime (in integer: julianDayNumber) is func 878 result 879 var time: aTime is time.value; 880 local 881 var integer: l is 0; 882 var integer: n is 0; 883 var integer: i is 0; 884 var integer: j is 0; 885 begin 886 l := julianDayNumber + 68569; 887 n := (4 * l) mdiv 146097; 888 l := l - (146097 * n + 3) mdiv 4; 889 i := (4000 * (l + 1)) mdiv 1461001; 890 l := l - (1461 * i) mdiv 4 + 31; 891 j := (80 * l) mdiv 2447; 892 aTime.day := l - (2447 * j) mdiv 80; 893 l := j mdiv 11; 894 aTime.month := j + 2 - (12 * l); 895 aTime.year := 100 * (n - 49) + i + l; 896 end func; 897 898 899(** 900 * Return a time expressed in seconds since the Unix Epoch. 901 * The Unix Epoch (1970-01-01 00:00:00 UTC) corresponds to 0. 902 * @return the seconds since the Unix Epoch. 903 *) 904const func integer: timestamp1970 (in time: aTime) is func 905 result 906 var integer: seconds is 0; 907 local 908 const integer: DAYS_FROM_0_TO_1970 is 719162; # Days between 0-01-01 and 1970-01-01 909 var integer: yearBefore is 0; 910 begin 911 yearBefore := pred(aTime.year); 912 seconds := (((yearBefore * 365 + 913 yearBefore mdiv 4 - 914 yearBefore mdiv 100 + 915 yearBefore mdiv 400 - 916 DAYS_FROM_0_TO_1970 + 917 pred(dayOfYear(aTime))) * 24 + 918 aTime.hour) * 60 + 919 aTime.minute) * 60 + 920 aTime.second - 921 aTime.timeZone * 60; 922 end func; 923 924 925(** 926 * Convert a timestamp into a time from the local time zone. 927 * The timestamp is expressed in seconds since the Unix Epoch. 928 * The Unix Epoch (1970-01-01 00:00:00 UTC) corresponds to 0. 929 * @return the local time that corresponds to the timestamp. 930 *) 931const func time: timestamp1970ToTime (in integer: timestamp) is func 932 result 933 var time: aTime is time.value; 934 begin 935 FROM_TIMESTAMP(timestamp, aTime.year, aTime.month, aTime.day, 936 aTime.hour, aTime.minute, aTime.second, 937 aTime.micro_second, aTime.timeZone, 938 aTime.daylightSavingTime); 939 end func; 940 941 942const func integer: timestamp1601 (in time: aTime) is func 943 result 944 var integer: nanosecs100 is 0; 945 local 946 const integer: SECONDS_1601_1970 is 11644473600; 947 const integer: NANOSECS100_TICK is 10000000; 948 begin 949 nanosecs100 := (timestamp1970(aTime) + SECONDS_1601_1970) * NANOSECS100_TICK; 950 end func; 951 952 953const func time: timestamp1601ToTime (in integer: timestamp) is func 954 result 955 var time: aTime is time.value; 956 local 957 const integer: SECONDS_1601_1970 is 11644473600; 958 const integer: NANOSECS100_TICK is 10000000; 959 var integer: utcSeconds is 0; 960 begin 961 utcSeconds := timestamp div NANOSECS100_TICK - SECONDS_1601_1970; 962 aTime := timestamp1970ToTime(utcSeconds); 963 end func; 964 965 966(* 967(*#### 968 * Convert a timestamp into Coordinated Universal Time (UTC). 969 *) 970const func time: timestamp1970ToUTC (in integer: timestamp) is func 971 result 972 var time: aTime is time.value; 973 local 974 const integer: SECS_DAY is 24 * 60 * 60; 975 var integer: dayclock is 0; 976 var integer: dayno is 0; 977 var integer: year is 1970; 978 begin 979 dayclock := timestamp mod SECS_DAY; 980 dayno := timestamp mdiv SECS_DAY; 981 aTime.second := dayclock rem 60; 982 aTime.minute := (dayclock rem 3600) div 60; 983 aTime.hour := dayclock div 3600; 984 if dayno >= 0 then 985 while dayno >= daysInYear(year) do 986 dayno -:= daysInYear(year); 987 incr(year); 988 end while; 989 aTime.month := 1; 990 while dayno >= daysInMonth(year, aTime.month) do 991 dayno -:= daysInMonth(year, aTime.month); 992 incr(aTime.month); 993 end while; 994 aTime.day := succ(dayno); 995 else 996 decr(year); 997 dayno := pred(-dayno); 998 while dayno >= daysInYear(year) do 999 dayno -:= daysInYear(year); 1000 decr(year); 1001 end while; 1002 aTime.month := 12; 1003 while dayno >= daysInMonth(year, aTime.month) do 1004 dayno -:= daysInMonth(year, aTime.month); 1005 decr(aTime.month); 1006 end while; 1007 aTime.day := daysInMonth(year, aTime.month) - dayno; 1008 end if; 1009 aTime.year := year; 1010 end func; 1011*) 1012 1013 1014(** 1015 * Compute pseudo-random time in the range [low, high]. 1016 * The random values are uniform distributed. 1017 * @return a random time such that low <= rand(low, high) and 1018 * rand(low, high) <= high holds. 1019 * @exception RANGE_ERROR The range is empty (low > high holds). 1020 *) 1021const func time: rand (in time: low, in time: high) is func 1022 result 1023 var time: randomTime is time.value; 1024 local 1025 var integer: timestampLow is 0; 1026 var integer: timestampHigh is 0; 1027 begin 1028 timestampLow := timestamp1970(low); 1029 timestampHigh := timestamp1970(high); 1030 randomTime := low; 1031 randomTime.second +:= rand(timestampLow, timestampHigh) - timestampLow; 1032 if randomTime.second = low.second then 1033 if randomTime.second = high.second then 1034 randomTime.micro_second := rand(low.micro_second, high.micro_second); 1035 else 1036 randomTime.micro_second := rand(low.micro_second, 999999); 1037 end if; 1038 elsif randomTime.second = high.second then 1039 randomTime.micro_second := rand(0, high.micro_second); 1040 else 1041 randomTime.micro_second := rand(0, 999999); 1042 end if; 1043 NORMALIZE(randomTime); 1044 end func; 1045 1046 1047(** 1048 * Determine the current local time. 1049 * @return the current local time. 1050 *) 1051const func time: time (NOW) is func 1052 result 1053 var time: localTime is time.value; 1054 begin 1055 GET_TIME(localTime.year, localTime.month, localTime.day, 1056 localTime.hour, localTime.minute, localTime.second, 1057 localTime.micro_second, localTime.timeZone, 1058 localTime.daylightSavingTime); 1059 end func; 1060 1061 1062(** 1063 * Return the specified UTC time. 1064 *) 1065const func time: time (in integer: year, in integer: month, in integer: day, 1066 in integer: hour, in integer: minute, in integer: second) is func 1067 result 1068 var time: utcTime is time.value; 1069 begin 1070 utcTime.year := year; 1071 utcTime.month := month; 1072 utcTime.day := day; 1073 utcTime.hour := hour; 1074 utcTime.minute := minute; 1075 utcTime.second := second; 1076 end func; 1077 1078 1079(** 1080 * Return the specified time in the specified timeZone. 1081 *) 1082const func time: timeInTimeZone (in integer: year, in integer: month, in integer: day, 1083 in integer: hour, in integer: minute, in integer: second, in integer: timeZone) is func 1084 result 1085 var time: aTime is time.value; 1086 begin 1087 aTime.year := year; 1088 aTime.month := month; 1089 aTime.day := day; 1090 aTime.hour := hour; 1091 aTime.minute := minute; 1092 aTime.second := second; 1093 aTime.timeZone := timeZone; 1094 end func; 1095 1096 1097(** 1098 * Return the specified UTC date. 1099 *) 1100const func time: date (in integer: year, in integer: month, in integer: day) is func 1101 result 1102 var time: aDate is time.value; 1103 begin 1104 aDate.year := year; 1105 aDate.month := month; 1106 aDate.day := day; 1107 end func; 1108 1109 1110(** 1111 * Wait until 'aTime' is reached 1112 *) 1113const proc: await (ref time: aTime) is func 1114 begin 1115 AWAIT_TIME(aTime.year, aTime.month, aTime.day, 1116 aTime.hour, aTime.minute, aTime.second, 1117 aTime.micro_second, aTime.timeZone); 1118 end func; 1119