HP Forums
A tiny New Year's programming challenge - Printable Version

+- HP Forums (https://www.hpmuseum.org/forum)
+-- Forum: HP Calculators (and very old HP Computers) (/forum-3.html)
+--- Forum: General Forum (/forum-4.html)
+--- Thread: A tiny New Year's programming challenge (/thread-2765.html)

Pages: 1 2


RE: A tiny New Year's programming challenge - Dieter - 01-07-2015 01:46 PM

(01-07-2015 08:19 AM)Werner Wrote:  Dieter: what's wrong with this? My first ever WP34S try! And no fiddling with flags.

There's nothing wrong with it – it works just as well. Your routine checks if the year starts or ends on a Thursday. Which is equivalent to testing if 2 Feb is a Monday OR 2 Feb of the next year is a Tuesday. Your program evaluates this as

    (weekday(2.2.yyyy) – 1)  *  (weekday(2.2.yyyy+1) – 2)

After omitting the initial CF.01 in my routine, both of our versions require 19 steps. Using NOT instead of SIGN +/– is a good idea – it will save another step. ;-)

I should add that I also wanted to use as few calls to date routines as possible in order to speed up the program for calculators that do not offer such functions, so that a single call of WDAY would be preferred. But for such calculators maybe a completely different approach would make more sense.

Thank you for your suggestion!

Dieter


RE: A tiny New Year's programming challenge - wynen - 01-22-2015 01:50 PM

I suppose, I'm a bit late for a "New Year's" programming challenge, but I want to contribute a HP-16C program to convert calender date representations.

My solution uses a subroutine to convert a (proleptic) Gregorian Calender date YYYY-MM-DD to a Julian Day Number (i.e. the Julian Date as of YYYY-MM-DDT12:00Z).
Code:

43,22,D     g LBL D     x=yyyy, y=mm, z=dd
34          x<->y       x=mm, y=yyyy, z=dd
3           [3]
30          -           m = mm-3
43,2        g x<0       m < 3 ?
21,0        GSB 0       => m = mm+12, y = yyyy-1
1           [1]
5           [5]
3           [3]
20          x   
2           [2]
40          +
5           [5]
10          ./.         x=(153m+2)/5, y=y, z=d, t=?
34          x<->y       x=y, y=(153m+2)/5, z=d, t=?
33          Rv          x=(153m+2)/5, y=d, z=?, t=y
40          +           x=(153m+2)/5+d, y=?, z=y, t=y
3           [3]
6           [6]
5           [5]
43,33       g R^        x=y, y=365, z=(153m+2)/5+d, t=?
20          x
43,36       g LSTx      x=y, y=365*y, z=(153m+2)/5+d, t=?
4           [4]
10          ./.         x=y/4, y=365*y, z=(153m+2)/5+d, t=?
40          +
43,36       g LSTx      x=y/4, y=365*y+y/4, z=(153m+2)/5+d, t=?
2           [2]
5           [5]
10          ./.         x=y/100, y=365*y+y/4, z=(153m+2)/5+d, t=?
30          -
43,36       g LSTx      x=y/100, y=365*y+y/4-y/100, z=(153m+2)/5+d, t=?
4           [4]
10          ./.         x=y/400, y=365*y+y/4-y/100, z=(153m+2)/5+d, t=?
40          +
40          +           x=d+(153m+2)/5+365*y+y/4-y/100+y/400
1           [1]
7           [7]
2           [2]
1           [1]
1           [1]
1           [1]
9           [9]
40          +           adjust for JD
43,21       g RTN

43,22,0     g LBL 0     x=mm-3, y=yyyy, z=dd
1           [1]
2           [2]
40          +   
34          x<->y
1           [1]
30          -
34          x<->y
43,21       g RTN       x=mm+12 y=yyyy-1

ISO 8601 calender date representations:
  • Calendar date: YYYY-MM-DD
    where YYYY represents a calendar year, MM the ordinal number of a calendar month within the calendar year, and DD the ordinal number of a calendar day within the calendar month
  • Ordinal date: YYYY-DDD
    where YYYY represents a calendar year and DDD the ordinal number of a calendar day within the calendar year
  • Week date: YYY-Www-D
    where YYYY represents a calendar year, W is the week designator, ww represents the ordinal number of a calendar week within the year, and D represents the ordinal number of a calendar day within the calendar week.

Convert calendar date to ordinal date or week date using the Julian Day Number JDN(YYYY-MM-DD).
  • Julian Day Number
    if mm > 2 then m := mm-3; y:=yyyy else m := mm+12; y := yyyy-1 endif
    JDN(yyyy-mm-dd) := 365*y + y/4 - y/100 + y/400 + (153*m+2)/5 + dd + 1721119
  • Ordinal number of a calendar day within the calendar week (Calendar day of the week, CDW)
    CDW = (JDN(yyyy-mm-dd) mod 7) + 1
  • Ordinal number of a calendar day within the calendar year (Calender day of the year, CDY)
    CDY = JDN(yyyy-mm-dd) - JDN(yyyy-01-01)
  • Ordinal number of a calendar week within the year (Calender week, CW)
    CW = [(CDY - CDW + 10) / 7]
    The expression (CDY-CDW+10) is constant for a calender week (1 <= CDW <= 7) and increments by 7 for the next week.
    For CW = 1 there is 7 <= (CDY - CDW + 10) <= 13
    For CDY = 1 (1st of January): If CW = 1 then CDW <= 4 (Thursday)
    Special cases:
    • (CDY - CDW + 10) / 7 == 0:
      the calendar day belongs to calendar week 52 or 53 of the preceding year.
      CW = 53 if the 1st of January of the preceding year is in calendar week 1 of the preceding year i.e. is Monday to Thursday, otherweise CW = 52.
    • (CDY - CDW + 10) / 7 == 53:
      the calendar day belongs to calendar week 1 of the next year, if the 1st of January of the next year is Tuesday to Thurday. Otherwise it is really CW 53.
Code:

43,22,C     g LBL C
44,0        STO 0       calendar year for ordinal date (YYYY-DDD)
44,5        STO 5       calender year for week date (YYYY-Www-D)
21,d        GSB D       compute Julian Day Number
44,4        STO 4
7           [7]         compute day of week
42,9        f RMD       CDW = (JDN mod 7) + 1
1           [1]    
40          +
44,3        STO 3
1           [1]
36          ENTER
36          ENTER
45,0        RCL 0
21,d        GSB D       compute Julian Date of 1st of January
45,4        RCL 4       compute day of year
34          x<->y       CDY = JDN(yyyy-mm-dd) - JDN(yyyy-01-01) + 1
30          -
1           [1]
40          +
44,1        STO 1       
45,3        RCL 3       compute week number 
30          -           CW = (CDY - CDW + 10) / 7
1           [1]
0           [0]
+           +
7           [7]
10          ./.
44,2        STO 2
43,30       g x>0       special calculation for CW=0?
22,1        GTO 1       No, check for CW 53
5           [5]
2           [2]
44,2        STO 2       assume CW =52
43,4,2      g SF 2      compute Calender Day of the Week
21,2        GSB 2       for the 1st of January of the preceding year
3           [3]
43,3        g x>y       if (YYYY-1)-01-01 > Thursday 
22,0        GTO 0       then CDW := 52; YYYY := YYYY-1;
5           [5]         else CDW := 53; YYYY := YYYY-1;
3           [3]
44,2        STO 2
22,0        GTO 0
43,22,1     g LBL 1
5           [5]
3           [3]
43,0        g x<>y      special calculation for CW=53?
22,0        GTO 0       No, display result
43,5,2      g CF 2      compute Calender Day of the Week
21,2        GSB 2       for the 1st of January of the next year
3           [3]
34          x<->y
43,3        g x>y       if (YYYY+1)-01-01 < Thursday
22,0        GTO 0       then CW := 53;
1           [1]         else CW := 1; YYYY := YYYY+1;
44,2        STO 2
45,0        RCL 0
40          + 
44,0        STO 5       
43,22,0     g LBL 0     show results
45,0        RCL 0       calendar year (YYYY)
1           [1]
0           [0]
0           [0]
0           [0]
20          x           (YYYY000)
45,1        RCL 1       Calender Day of the Year (1...365/366)
40          +           ISO 8601 ordinal date: YYYYDDD
45,3        RCL 3       Calender day of the Week (1=Mon,...,7=Sun)
45,2        RCL 2       Calender Week of the Year (1..52/53)
45,5        RCL 5       Calender Year (corrected for Calender Week)
43,21       g RTN
            
43,22,2     g LBL 2     Compute Calender Day of Week for the 1st of January
1           [1]         of the preceding year (flag 2 set)
36          ENTER       or the next year (flag 2 cleared)
36          ENTER       
43,6,2      g F? 2
49          CHS
45,0        RCL 0       x = YYYY
40          +           x = (Flag 2 set) ? YYYY-1 : YYYY+1
43,6,2      g F? 2
44,0        STO 5       Store YYYY-1
21,d        GSB D       JDN(x-01-01)
7           [7]
42,9        f RMD       CDW (0=Mon,...,3=Thu,...,6=Sun)
43,21       g RTN

Registers:
R0 = calendar year (YYYY)
R1 = calendar day of the year (DDD)
R2 = calendar week (ww)
R3 = calendar day of the week (D)
R4 = julian day number
R5 = calendar year for week date (R0+-1)

Usage:
DEC
{DD}
ENTER
{MM}
ENTER
{YYYY}
GSB C

Display result:
YYYY (corrected for calender week)
Rdown
ww (calender week: 1-53)
Rdown
D (weekday (1=Mon,...,7=Sun)
Rdown
YYYYDDD (ISO 8601 ordinal date)

Comments are welcome

Hartmut


RE: A tiny New Year's programming challenge - Damien - 01-22-2015 11:24 PM

Hi everyone !

Something for HP PRIME:
Code:

//--------------------------------------
// Convert gregorian calendar dates
// YYYY.MMDD into yyyy-Www-d 
// according to ISO 8601
// V0.1 (2015-W04-4)
//--------------------------------------
EXPORT ISOWeekDate(date) 
BEGIN

  LOCAL year,day,JDn;
  LOCAL ISOWeekNb,ISOd,ISOYear;

// cut date YYYY.MMDD
  year:=IP(date); // YYYY
  day:=IP(FP(FP(date)*100)*100); // DD
  ISOYear:=IP(date); // YYYY

// Find julian day number at noon
// from the gregorian date YYYY.MMDD
  JDn:=DateG2JJ(date);

// Find day number according to ISO8601
// (Mo=1,Tue=2,...,Sun=7)
  ISOd:=(JDn MOD 7)+1;

// Find week number according to ISO8601
// (1≤WeekNb≤53)
  ISOWeekNb:=ISOWeekNum(JDn);

// Find year according to ISO8601 
// - for 'normal' week: 2≤Week≤51
// - for 'pivotal' week:1,52,53
  IF 1<ISOWeekNb<52 THEN
   ISOYear:=IP(date);
  ELSE
// possible week transitions between 
// year N and N+1
//+---------+--+--+--+--+--+--+--+-+-------------
//|Monday   |22|23|24|25|26|27|28|1|
//|Tuesday  |23|24|25|26|27|28|29|2|    THE
//|Wednesday|24|25|26|27|28|29|30|3|LAST WEEK OF
//|Thursday |25|26|27|28|29|30|31|4|  YEAR N-1
//|Friday   |26|27|28|29|30|31|01|5|
//|Saturday |27|28|29|30|31|01|02|6|(WEEK 52, 53)
//|Sunday   |28|29|30|31|01|02|03|7|
//+---------+--+--+--+--+--+--+--+-+-------------
//|Monday   |29|30|31|01|02|03|04|1|
//|Tuesday  |30|31|01|02|03|04|05|2|    THE
//|Wednesday|31|01|02|03|04|05|06|3|FIRST WEEK OF
//|Thursday |01|02|03|04|05|06|07|4|   YEAR N
//|Friday   |02|03|04|05|06|07|08|5|
//|Saturday |03|04|05|06|07|08|09|6|  (WEEK 1)
//|Sunday   |04|05|06|07|08|09|10|7|
//+---------+--+--+--+--+--+--+--+-+-------------
//                 ⇳
// Now lets translate this into
// some (many) lines of code
   IF ISOWeekNb==1 THEN // first week ?
    IF ISOd==1 THEN // Monday ?
     IF 29≤day≤31 THEN
       ISOYear:=IP(date)+1; // YYYY+1
     ELSE
      IF 1≤day≤4 THEN
        ISOYear:=IP(date); // YYYY
      END;
     END;
    END; // Monday 
    IF ISOd==2 THEN // Tuesday ?
     IF 30≤day≤31 THEN
       ISOYear:=IP(date)+1; // YYYY+1
     ELSE
      IF 1≤day≤5 THEN
        ISOYear:=IP(date); // YYYY
      END;
     END;
    END; // Tuesday
    IF ISOd==3 THEN // Wednesday ?
     IF day==31 THEN
      ISOYear:=IP(date)+1; // YYYY+1
     ELSE
      IF 1≤day≤6 THEN
       ISOYear:=IP(date); // YYYY
      END; 
     END;
    END; // Wednesday
    IF ISOd==4 THEN // Thursday ?
     IF 1≤day≤7 THEN
      ISOYear:=IP(date); // YYYY
     END;
    END; // Thursday
    IF ISOd==5 THEN // Friday ?
     IF 2≤day≤8 THEN
      ISOYear:=IP(date); // YYYY 
     END;
    END; // Friday
    IF ISOd==6 THEN // Saturday ? 
     IF 3≤day≤9 THEN
      ISOYear:=IP(date); // YYYY
     END;
    END; // Saturday
    IF ISOd==7 THEN // Sunday ?
     IF 4≤day≤10 THEN
      ISOYear:=IP(date); // YYYY
     END;
    END; // Sunday
    ELSE // Week 1
     IF 52≤ISOWeekNb≤53 THEN // last week ?
      IF ISOd==1 THEN // Monday ?
       IF 22≤day≤28 THEN
        ISOYear:=IP(date); // YYYY
       END;
      END; // Monday
      IF ISOd==2 THEN // Tuesday ?
       IF 23≤day≤29 THEN
        ISOYear:=IP(date); // YYYY
       END;
      END; // Tuesday
      IF ISOd==3 THEN // Wednesday ?
       IF 24≤day≤30 THEN
        ISOYear:=IP(date); // YYYY
       END;
      END; // Wednesday
      IF ISOd==4 THEN // Thursday ?
       IF 25≤day≤31 THEN
        ISOYear:=IP(date); // YYYY
       END;
      END;
      IF ISOd==5 THEN // Friday ?
       IF 26≤day≤31 THEN
        ISOYear:=IP(date); // YYYY
       ELSE
        IF day==1 THEN
         ISOYear:=IP(date)-1; // YYYY-1
        END;
       END;
      END;
      IF ISOd==6 THEN // Saturday ?
       IF 27≤day≤31 THEN
        ISOYear:=IP(date); // YYYY
       ELSE
        IF 1≤day≤2 THEN 
         ISOYear:=IP(date)-1; // YYYY-1
        END;
       END;
      END;
      IF ISOd==7 THEN // Sunday ?
       IF 28≤day≤31 THEN
        ISOYear:=IP(date); // YYYY
       ELSE
        IF 1≤day≤3 THEN 
         ISOYear:=IP(date)-1; // YYYY-1
        END;
       END;
      END;  // Sunday ?
     END;  // 52≤week≤53 ?
    END;  // week 1 ? 
   END;  // 2≤week≤51 ?

// ( Phew ! Thanks CUT/COPY/PAST ! )

//  TEXT Formatting
   IF ISOWeekNb<10 THEN
    RETURN ISOYear+"-W0"+ISOWeekNb+"-"+ISOd;
   ELSE
    RETURN ISOYear+"-W"+ISOWeekNb+"-"+ISOd;
   END;

END; // Begin


//----------------------------------------------------
// Convert calendar (Gregorian) date
// to julian day number JDn at noon
// aaaa.mmjj => JDn
// e.g. 2000.0101 => 2451545 (J2000)
//----------------------------------------------------
DateG2JJ(date) // for date ≥ oct 15 1582 (2299161)
BEGIN

 LOCAL year,month,day;
 LOCAL cnt,diz;

// Cut the date YYYY.MMDD into sub parts
  year:=IP(date); // YYYY
  month:=IP(100*FP(date)); // MM
  day:=IP(FP(FP(date)*100)*100); // DD

// Move the beginning of the year to
// March 1st (adding 1 day, e.g. feb 29th
// becomes easier if at the end)
  IF month<3 THEN
   year:=year-1;
   month:=month+12;
  END;

// Cut the year into parts
// (Needed for converting Gregorian
// calendar dates to JDN)
  cnt:=IP(year/100); // [YY]YY
  diz:=IP(year-100*IP(year/100)); //YY[YY]

// julian day number at noon
// from gregorian date YYYY.MMDD
  RETURN IP((146097*cnt+6884480)/4)
         +IP(1461*diz/4)
         +IP((153*month-457)/5)
         +day-1;
END;


//----------------------------------------------------
// Return the week number [1 - 52 sometimes 53] 
// according to ISO 8601 from the julian day number 
// (JDn≥2299161)
//----------------------------------------------------
ISOWeekNum(JD)
BEGIN
  LOCAL d4;
  d4:=(JD+31741-(JD MOD 7)) MOD 146097 
      MOD 36524 MOD 1461;
  RETURN IP(((d4-IP(d4/1460)) MOD 365
         +IP(d4/1460))/7+1);
END;
Convert calendar date YYYY.MMDD to ISO 8601 date yyyy-Www-d.

e.g. 2013.1231 => 2014-W01-2

Regards,

Damien


RE: A tiny New Year's programming challenge - Dieter - 01-23-2015 08:25 PM

(01-22-2015 01:50 PM)wynen Wrote:  I suppose, I'm a bit late for a "New Year's" programming challenge, but I want to contribute a HP-16C program to convert calender date representations.

Thank you very much for your contribution. I do not have access to a 16C, so I cannot try your program. I assume it is based on integer arithmetics. Does it also work for negative (B.C.) years?

(01-22-2015 01:50 PM)wynen Wrote:  My solution uses a subroutine to convert a (proleptic) Gregorian Calender date YYYY-MM-DD to a Julian Day Number (i.e. the Julian Date as of YYYY-MM-DDT12:00Z).
(...)
  • Julian Day Number
    if mm > 2 then m := mm-3; y:=yyyy else m := mm+12; y := yyyy-1 endif
    JDN(yyyy-mm-dd) := 365*y + y/4 - y/100 + y/400 + (153*m+2)/5 + dd + 1721119

I think it's not mm+12 in the "else" branch, but mm+9. This is also what your program calculates: it first evaluates mm–3 and, if this is <0 (i.e. mm=1..2) it adds 12 back, giving in total mm+9.

(01-22-2015 01:50 PM)wynen Wrote:  
  • Ordinal number of a calendar day within the calendar year (Calender day of the year, CDY)
    CDY = JDN(yyyy-mm-dd) - JDN(yyyy-01-01)

+1, I would say, or use JDN(yyyy-01-00). Otherwise 1 Jan is day 0.

(01-22-2015 01:50 PM)wynen Wrote:  
  • Ordinal number of a calendar week within the year (Calender week, CW)

I think I''ll have to take a closer look at that. Have you tried the examples in my initial post?

Dieter


RE: A tiny New Year's programming challenge - Dieter - 01-23-2015 08:39 PM

(01-22-2015 11:24 PM)Damien Wrote:  Something for HP PRIME:

Thank you for this elaborate version with a completely different approach, translating week tables into a more than complex if-then-else sequence. The formula in the weeknumber routine looks interesting as well.

Dieter


RE: A tiny New Year's programming challenge - Damien - 01-23-2015 09:47 PM

(01-23-2015 08:39 PM)Dieter Wrote:  
(01-22-2015 11:24 PM)Damien Wrote:  Something for HP PRIME:

Thank you for this elaborate version with a completely different approach, translating week tables into a more than complex if-then-else sequence. The formula in the weeknumber routine looks interesting as well.

Dieter

Thanks Dieter,

the first time i saw this formula, I immediately thought to the GREAT WP34s and its JDN function.

the weeknumber formula was found here:
http://www.auduteau.net/calendar/cal5.shtml ( in french Sorry ! )
But it seems to be the translation of (in english):
http://www.tondering.dk/claus/cal/week.php
(unfortunately where the weeknumber formula has disappeared... And has been remplaced by another formula (the author trial ? - That in some cases don't gives the good answer).

Regards,

Damien.


RE: A tiny New Year's programming challenge - Dieter - 01-23-2015 10:07 PM

(01-23-2015 09:47 PM)Damien Wrote:  the weeknumber formula was found here:
http://www.auduteau.net/calendar/cal5.shtml ( in french Sorry ! )

No need to apologize. The formula indeed looks promising. I tried some test cases with the 34s and the results look fine. However, a proof or at least an explanation how it works would be nice.

Dieter