From e95c13b1c6cbc03c4745b4aef665afa3f713a63b Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Tue, 6 Jan 2026 19:15:24 -0800 Subject: [PATCH] moon phase calculation based on phoon --- ARCHITECTURE.md | 1 + MISSING_FEATURES.md | 13 +- build.zig | 27 + build.zig.zon | 1 + libs/phoon_14Aug2014/Makefile | 33 + libs/phoon_14Aug2014/README | 50 ++ libs/phoon_14Aug2014/astro.c | 467 +++++++++++++ libs/phoon_14Aug2014/astro.h | 46 ++ libs/phoon_14Aug2014/date_parse.c | 1048 +++++++++++++++++++++++++++++ libs/phoon_14Aug2014/date_parse.h | 33 + libs/phoon_14Aug2014/phoon.1 | 23 + libs/phoon_14Aug2014/phoon.c | 575 ++++++++++++++++ src/Moon.zig | 71 ++ src/http/help.zig | 4 +- src/render/Custom.zig | 13 +- 15 files changed, 2394 insertions(+), 11 deletions(-) create mode 100644 libs/phoon_14Aug2014/Makefile create mode 100644 libs/phoon_14Aug2014/README create mode 100644 libs/phoon_14Aug2014/astro.c create mode 100644 libs/phoon_14Aug2014/astro.h create mode 100644 libs/phoon_14Aug2014/date_parse.c create mode 100644 libs/phoon_14Aug2014/date_parse.h create mode 100644 libs/phoon_14Aug2014/phoon.1 create mode 100644 libs/phoon_14Aug2014/phoon.c create mode 100644 src/Moon.zig diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index b96847b..2af72b2 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -343,6 +343,7 @@ The application makes network calls to the following services: **External (other):** - Airport code -> location mapping: [Openflights](https://github.com/jpatokal/openflights) - Ip address -> location mapping: [GeoLite2 City Database](https://github.com/maxmind/libmaxminddb) +- Moon phase calculations (vendored): [Phoon](https://acme.com/software/phoon/) ## Performance Targets diff --git a/MISSING_FEATURES.md b/MISSING_FEATURES.md index 1a683f6..41024db 100644 --- a/MISSING_FEATURES.md +++ b/MISSING_FEATURES.md @@ -20,15 +20,14 @@ Features not yet implemented in the Zig version: - lang query parameter support - Translation of weather conditions and text (54 languages) -## 5. Moon Phase Calculation -- Real moon phase computation based on date -- Moon phase emoji display -- Moonday calculation - -## 6. Astronomical Times +## 5. Astronomical Times - Calculate dawn, sunrise, zenith, sunset, dusk times - Based on location coordinates and timezone - Display in custom format output -## 7. Json output +## 6. Json output - Does not match wttr.in format + +## 7. Moon endpoint +- `/Moon` and `/Moon@YYYY-MM-DD` endpoints not yet implemented +- Moon phase calculation is implemented and available in custom format (%m, %M) diff --git a/build.zig b/build.zig index 3a8eca2..9b2b6b5 100644 --- a/build.zig +++ b/build.zig @@ -19,6 +19,29 @@ pub fn build(b: *std.Build) void { const maxminddb_upstream = b.dependency("maxminddb", .{}); + // Build phoon as a static library + const phoon = b.addLibrary(.{ + .name = "phoon", + .linkage = .static, + .root_module = b.createModule(.{ + .target = target, + .optimize = optimize, + .link_libc = true, + }), + }); + + phoon.addIncludePath(b.path("libs/phoon_14Aug2014")); + phoon.addCSourceFiles(.{ + .root = b.path("libs/phoon_14Aug2014"), + .files = &.{ + "astro.c", + "date_parse.c", + }, + .flags = &.{ "-std=c99", "-D_DEFAULT_SOURCE" }, + }); + phoon.linkLibC(); + phoon.linkSystemLibrary("m"); + // Build libmaxminddb as a static library const maxminddb = b.addLibrary(.{ .name = "maxminddb", @@ -74,8 +97,10 @@ pub fn build(b: *std.Build) void { }); exe.root_module.addOptions("build_options", build_options); exe.root_module.addIncludePath(maxminddb_upstream.path("include")); + exe.root_module.addIncludePath(b.path("libs/phoon_14Aug2014")); exe.root_module.addConfigHeader(maxminddb_config); exe.linkLibrary(maxminddb); + exe.linkLibrary(phoon); exe.linkLibC(); b.installArtifact(exe); @@ -109,8 +134,10 @@ pub fn build(b: *std.Build) void { }); tests.root_module.addOptions("build_options", test_options); tests.root_module.addIncludePath(maxminddb_upstream.path("include")); + tests.root_module.addIncludePath(b.path("libs/phoon_14Aug2014")); tests.root_module.addConfigHeader(maxminddb_config); tests.linkLibrary(maxminddb); + tests.linkLibrary(phoon); tests.linkLibC(); const run_tests = b.addRunArtifact(tests); diff --git a/build.zig.zon b/build.zig.zon index de5197b..4bca8e2 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -18,6 +18,7 @@ .url = "git+https://github.com/rockorager/zeit?ref=zig-0.15#7ac64d72dbfb1a4ad549102e7d4e232a687d32d8", .hash = "zeit-0.6.0-5I6bk36tAgATpSl9wjFmRPMqYN2Mn0JQHgIcRNcqDpJA", }, + .phoon = .{ .path = "libs/phoon_14Aug2014" }, }, .fingerprint = 0x710c2b57e81aa678, .minimum_zig_version = "0.15.2", diff --git a/libs/phoon_14Aug2014/Makefile b/libs/phoon_14Aug2014/Makefile new file mode 100644 index 0000000..9d81597 --- /dev/null +++ b/libs/phoon_14Aug2014/Makefile @@ -0,0 +1,33 @@ +# Makefile for phoon + +BINDIR = /usr/local/bin +MANDIR = /usr/local/man/man1 + +DEFINES = -DOS_BSD +#DEFINES = -DOS_SYSV + +CC = cc +CFLAGS = -O $(DEFINES) -ansi -pedantic -U__STRICT_ANSI__ -Wall -Wpointer-arith -Wshadow -Wcast-qual -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wredundant-decls -Wno-long-long + +LDFLAGS = -s + + +all: phoon + +phoon: phoon.o date_parse.o astro.o + $(CC) $(LDFLAGS) -o phoon phoon.o date_parse.o astro.o -lm + +.c.o: + $(CC) $(CFLAGS) -c $< + +date_parse.o: date_parse.h +astro.o: astro.h + +install: all + rm -f $(BINDIR)/phoon + cp phoon $(BINDIR) + rm -f $(MANDIR)/phoon.1 + cp phoon.1 $(MANDIR) + +clean: + rm -f phoon *.o a.out core diff --git a/libs/phoon_14Aug2014/README b/libs/phoon_14Aug2014/README new file mode 100644 index 0000000..4598879 --- /dev/null +++ b/libs/phoon_14Aug2014/README @@ -0,0 +1,50 @@ + phoon - display current moon phase + + +phoon - program to display the PHase of the mOON. Unlike other +such programs, which just tell you how long since first quarter +or something like that, phoon *shows* you the phase with a little +picture. I've put an example at the end of this file. I first +wrote this program in Pascal / TOPS-20 at CMU in 1979; I translated +it to Ratfor / Software Tools in 1981; and now it's in C / Unix. + +Files in this distribution: + + README this + Makefile guess + phoon.c phase of moon display + astro.c phase of moon calculations + astro.h header file + phoon.1 manual for phase of moon program + date_parse.c date-parsing routine + date_parse.h header file + +Unpack the files, edit Makefile and change the options to suit, make, +and enjoy! Feedback is welcome - send bug reports, enhancements, +checks, money orders, etc. to the addresses below. + + Jef Poskanzer jef@mail.acme.com http://www.acme.com/jef/ + + .-- + .-- + .-' + .-'@ + /@@@ + ./ + /@@ o + /@@@@ + |@@@@@ + /@@@@@ Last Quarter + + | @@@@ 4 1:36:10 + |@ @@@ New Moon - + | 3 7:34:53 + \ . @ + | + \ @ + \ o + `\ + \ + `-. + `-. + `-- + `-- diff --git a/libs/phoon_14Aug2014/astro.c b/libs/phoon_14Aug2014/astro.c new file mode 100644 index 0000000..9e69536 --- /dev/null +++ b/libs/phoon_14Aug2014/astro.c @@ -0,0 +1,467 @@ +/* Adapted from "moontool.c" by John Walker, Release 2.5 +** +** Quoting from the original: +** +** The algorithms used in this program to calculate the positions Sun and +** Moon as seen from the Earth are given in the book "Practical Astronomy +** With Your Calculator" by Peter Duffett-Smith, Second Edition, +** Cambridge University Press, 1981. Ignore the word "Calculator" in the +** title; this is an essential reference if you're interested in +** developing software which calculates planetary positions, orbits, +** eclipses, and the like. If you're interested in pursuing such +** programming, you should also obtain: +** +** "Astronomical Formulae for Calculators" by Jean Meeus, Third Edition, +** Willmann-Bell, 1985. A must-have. +** +** "Planetary Programs and Tables from -4000 to +2800" by Pierre +** Bretagnon and Jean-Louis Simon, Willmann-Bell, 1986. If you want the +** utmost (outside of JPL) accuracy for the planets, it's here. +** +** "Celestial BASIC" by Eric Burgess, Revised Edition, Sybex, 1985. Very +** cookbook oriented, and many of the algorithms are hard to dig out of +** the turgid BASIC code, but you'll probably want it anyway. +** +** See http://www.fourmilab.ch/moontool/ +*/ + +#include +#include +#include +#include + +#include "astro.h" + + +#define FALSE 0 +#define TRUE 1 + +/* Astronomical constants */ + +#define epoch 2444238.5 /* 1980 January 0.0 */ + +/* Constants defining the Sun's apparent orbit */ + +#define elonge 278.833540 /* Ecliptic longitude of the Sun + at epoch 1980.0 */ +#define elongp 282.596403 /* Ecliptic longitude of the Sun at + perigee */ +#define eccent 0.016718 /* Eccentricity of Earth's orbit */ +#define sunsmax 1.495985e8 /* Semi-major axis of Earth's orbit, km */ +#define sunangsiz 0.533128 /* Sun's angular size, degrees, at + semi-major axis distance */ + +/* Elements of the Moon's orbit, epoch 1980.0 */ + +#define mmlong 64.975464 /* Moon's mean lonigitude at the epoch */ +#define mmlongp 349.383063 /* Mean longitude of the perigee at the + epoch */ +#define mlnode 151.950429 /* Mean longitude of the node at the + epoch */ +#define minc 5.145396 /* Inclination of the Moon's orbit */ +#define mecc 0.054900 /* Eccentricity of the Moon's orbit */ +#define mangsiz 0.5181 /* Moon's angular size at distance a + from Earth */ +#define msmax 384401.0 /* Semi-major axis of Moon's orbit in km */ +#define mparallax 0.9507 /* Parallax at distance a from Earth */ +#define synmonth 29.53058868 /* Synodic month (new Moon to new Moon) */ +#define lunatbase 2423436.0 /* Base date for E. W. Brown's numbered + series of lunations (1923 January 16) */ + +/* Properties of the Earth */ + +#define earthrad 6378.16 /* Radius of Earth in kilometres */ + + +#define PI 3.14159265358979323846 /* Assume not near black hole nor in + Tennessee */ + +/* Handy mathematical functions */ + +#define sgn(x) (((x) < 0) ? -1 : ((x) > 0 ? 1 : 0)) /* Extract sign */ +#define abs(x) ((x) < 0 ? (-(x)) : (x)) /* Absolute val */ +#define fixangle(a) ((a) - 360.0 * (floor((a) / 360.0))) /* Fix angle */ +#define torad(d) ((d) * (PI / 180.0)) /* Deg->Rad */ +#define todeg(d) ((d) * (180.0 / PI)) /* Rad->Deg */ +#define dsin(x) (sin(torad((x)))) /* Sin from deg */ +#define dcos(x) (cos(torad((x)))) /* Cos from deg */ + + +/* + * UNIX_TO_JULIAN -- Convert internal Unix date/time to astronomical + * Julian time (i.e. Julian date plus day fraction, expressed as + * a double). + */ +double +unix_to_julian( time_t t ) +{ + return (double) t / 86400.0 + 2440587.4999996666666666666; +} + + +/* + * JYEAR -- Convert Julian date to year, month, day, which are + * returned via integer pointers to integers. + */ +static void +jyear( double td, int* yy, int* mm, int* dd ) +{ + double j, d, y, m; + + td += 0.5; /* Astronomical to civil */ + j = floor(td); + j = j - 1721119.0; + y = floor(((4 * j) - 1) / 146097.0); + j = (j * 4.0) - (1.0 + (146097.0 * y)); + d = floor(j / 4.0); + j = floor(((4.0 * d) + 3.0) / 1461.0); + d = ((4.0 * d) + 3.0) - (1461.0 * j); + d = floor((d + 4.0) / 4.0); + m = floor(((5.0 * d) - 3) / 153.0); + d = (5.0 * d) - (3.0 + (153.0 * m)); + d = floor((d + 5.0) / 5.0); + y = (100.0 * y) + j; + if (m < 10.0) + m = m + 3; + else { + m = m - 9; + y = y + 1; + } + *yy = y; + *mm = m; + *dd = d; +} + + +/* + * MEANPHASE -- Calculates time of the mean new Moon for a given + * base date. This argument K to this function is + * the precomputed synodic month index, given by: + * + * K = (year - 1900) * 12.3685 + * + * where year is expressed as a year and fractional year. + */ +static double +meanphase( double sdate, double k ) +{ + double t, t2, t3, nt1; + + /* Time in Julian centuries from 1900 January 0.5 */ + t = (sdate - 2415020.0) / 36525; + t2 = t * t; /* Square for frequent use */ + t3 = t2 * t; /* Cube for frequent use */ + + nt1 = 2415020.75933 + synmonth * k + + 0.0001178 * t2 + - 0.000000155 * t3 + + 0.00033 * dsin(166.56 + 132.87 * t - 0.009173 * t2); + + return nt1; +} + + +/* + * TRUEPHASE -- Given a K value used to determine the + * mean phase of the new moon, and a phase + * selector (0.0, 0.25, 0.5, 0.75), obtain + * the true, corrected phase time. + */ +static double +truephase( double k, double pha ) +{ + double t, t2, t3, pt, m, mprime, f; + int apcor = FALSE; + + k += pha; /* Add phase to new moon time */ + t = k / 1236.85; /* Time in Julian centuries from + 1900 January 0.5 */ + t2 = t * t; /* Square for frequent use */ + t3 = t2 * t; /* Cube for frequent use */ + pt = 2415020.75933 /* Mean time of phase */ + + synmonth * k + + 0.0001178 * t2 + - 0.000000155 * t3 + + 0.00033 * dsin(166.56 + 132.87 * t - 0.009173 * t2); + + m = 359.2242 /* Sun's mean anomaly */ + + 29.10535608 * k + - 0.0000333 * t2 + - 0.00000347 * t3; + mprime = 306.0253 /* Moon's mean anomaly */ + + 385.81691806 * k + + 0.0107306 * t2 + + 0.00001236 * t3; + f = 21.2964 /* Moon's argument of latitude */ + + 390.67050646 * k + - 0.0016528 * t2 + - 0.00000239 * t3; + if ((pha < 0.01) || (abs(pha - 0.5) < 0.01)) { + + /* Corrections for New and Full Moon */ + + pt += (0.1734 - 0.000393 * t) * dsin(m) + + 0.0021 * dsin(2 * m) + - 0.4068 * dsin(mprime) + + 0.0161 * dsin(2 * mprime) + - 0.0004 * dsin(3 * mprime) + + 0.0104 * dsin(2 * f) + - 0.0051 * dsin(m + mprime) + - 0.0074 * dsin(m - mprime) + + 0.0004 * dsin(2 * f + m) + - 0.0004 * dsin(2 * f - m) + - 0.0006 * dsin(2 * f + mprime) + + 0.0010 * dsin(2 * f - mprime) + + 0.0005 * dsin(m + 2 * mprime); + apcor = TRUE; + } else if ((abs(pha - 0.25) < 0.01 || (abs(pha - 0.75) < 0.01))) { + pt += (0.1721 - 0.0004 * t) * dsin(m) + + 0.0021 * dsin(2 * m) + - 0.6280 * dsin(mprime) + + 0.0089 * dsin(2 * mprime) + - 0.0004 * dsin(3 * mprime) + + 0.0079 * dsin(2 * f) + - 0.0119 * dsin(m + mprime) + - 0.0047 * dsin(m - mprime) + + 0.0003 * dsin(2 * f + m) + - 0.0004 * dsin(2 * f - m) + - 0.0006 * dsin(2 * f + mprime) + + 0.0021 * dsin(2 * f - mprime) + + 0.0003 * dsin(m + 2 * mprime) + + 0.0004 * dsin(m - 2 * mprime) + - 0.0003 * dsin(2 * m + mprime); + if (pha < 0.5) + /* First quarter correction */ + pt += 0.0028 - 0.0004 * dcos(m) + 0.0003 * dcos(mprime); + else + /* Last quarter correction */ + pt += -0.0028 + 0.0004 * dcos(m) - 0.0003 * dcos(mprime); + apcor = TRUE; + } + if (!apcor) { + (void)fprintf (stderr, + "TRUEPHASE called with invalid phase selector.\n"); + abort(); + } + return pt; +} + + +/* + * PHASEHUNT5 -- Find time of phases of the moon which surround + * the current date. Five phases are found, starting + * and ending with the new moons which bound the + * current lunation. + */ +void +phasehunt5( double sdate, double phases[5] ) +{ + double adate, k1, k2, nt1, nt2; + int yy, mm, dd; + + adate = sdate - 45; + + jyear(adate, &yy, &mm, &dd); + k1 = floor((yy + ((mm - 1) * (1.0 / 12.0)) - 1900) * 12.3685); + + adate = nt1 = meanphase(adate, k1); + while (TRUE) { + adate += synmonth; + k2 = k1 + 1; + nt2 = meanphase(adate, k2); + if (nt1 <= sdate && nt2 > sdate) + break; + nt1 = nt2; + k1 = k2; + } + phases [0] = truephase (k1, 0.0); + phases [1] = truephase (k1, 0.25); + phases [2] = truephase (k1, 0.5); + phases [3] = truephase (k1, 0.75); + phases [4] = truephase (k2, 0.0); +} + + +/* + * PHASEHUNT2 -- Find time of phases of the moon which surround + * the current date. Two phases are found. + */ +void +phasehunt2( double sdate, double phases[2], double which[2] ) +{ + double phases5[5]; + + phasehunt5( sdate, phases5 ); + phases[0] = phases5[0]; + which[0] = 0.0; + phases[1] = phases5[1]; + which[1] = 0.25; + if ( phases[1] <= sdate ) { + phases[0] = phases[1]; + which[0] = which[1]; + phases[1] = phases5[2]; + which[1] = 0.5; + if ( phases[1] <= sdate ) { + phases[0] = phases[1]; + which[0] = which[1]; + phases[1] = phases5[3]; + which[1] = 0.75; + if ( phases[1] <= sdate ) { + phases[0] = phases[1]; + which[0] = which[1]; + phases[1] = phases5[4]; + which[1] = 0.0; + } + } + } +} + + +/* + * KEPLER -- Solve the equation of Kepler. + */ +static double +kepler( double m, double ecc ) +{ + double e, delta; +#define EPSILON 1E-6 + + e = m = torad(m); + do { + delta = e - ecc * sin(e) - m; + e -= delta / (1 - ecc * cos(e)); + } while (abs (delta) > EPSILON); + return e; +} + + +/* + * PHASE -- Calculate phase of moon as a fraction: + * + * The argument is the time for which the phase is requested, + * expressed as a Julian date and fraction. Returns the terminator + * phase angle as a percentage of a full circle (i.e., 0 to 1), + * and stores into pointer arguments the illuminated fraction of + * the Moon's disc, the Moon's age in days and fraction, the + * distance of the Moon from the centre of the Earth, and the + * angular diameter subtended by the Moon as seen by an observer + * at the centre of the Earth. + * + * pphase: Illuminated fraction + * mage: Age of moon in days + * dist: Distance in kilometres + * angdia: Angular diameter in degrees + * sudist: Distance to Sun + * suangdia: Sun's angular diameter + */ +double +phase( double pdate, double* pphase, double* mage, double* dist, double* angdia, double* sudist, double* suangdia ) +{ + + double Day, N, M, Ec, Lambdasun, ml, MM, MN, Ev, Ae, A3, MmP, + mEc, A4, lP, V, lPP, NP, y, x, Lambdamoon, BetaM, + MoonAge, MoonPhase, + MoonDist, MoonDFrac, MoonAng, MoonPar, + F, SunDist, SunAng; + + /* Calculation of the Sun's position */ + + Day = pdate - epoch; /* Date within epoch */ + N = fixangle((360 / 365.2422) * Day); /* Mean anomaly of the Sun */ + M = fixangle(N + elonge - elongp); /* Convert from perigee + co-ordinates to epoch 1980.0 */ + Ec = kepler(M, eccent); /* Solve equation of Kepler */ + Ec = sqrt((1 + eccent) / (1 - eccent)) * tan(Ec / 2); + Ec = 2 * todeg(atan(Ec)); /* True anomaly */ + Lambdasun = fixangle(Ec + elongp); /* Sun's geocentric ecliptic + longitude */ + /* Orbital distance factor */ + F = ((1 + eccent * cos(torad(Ec))) / (1 - eccent * eccent)); + SunDist = sunsmax / F; /* Distance to Sun in km */ + SunAng = F * sunangsiz; /* Sun's angular size in degrees */ + + + /* Calculation of the Moon's position */ + + /* Moon's mean longitude */ + ml = fixangle(13.1763966 * Day + mmlong); + + /* Moon's mean anomaly */ + MM = fixangle(ml - 0.1114041 * Day - mmlongp); + + /* Moon's ascending node mean longitude */ + MN = fixangle(mlnode - 0.0529539 * Day); + + /* Evection */ + Ev = 1.2739 * sin(torad(2 * (ml - Lambdasun) - MM)); + + /* Annual equation */ + Ae = 0.1858 * sin(torad(M)); + + /* Correction term */ + A3 = 0.37 * sin(torad(M)); + + /* Corrected anomaly */ + MmP = MM + Ev - Ae - A3; + + /* Correction for the equation of the centre */ + mEc = 6.2886 * sin(torad(MmP)); + + /* Another correction term */ + A4 = 0.214 * sin(torad(2 * MmP)); + + /* Corrected longitude */ + lP = ml + Ev + mEc - Ae + A4; + + /* Variation */ + V = 0.6583 * sin(torad(2 * (lP - Lambdasun))); + + /* True longitude */ + lPP = lP + V; + + /* Corrected longitude of the node */ + NP = MN - 0.16 * sin(torad(M)); + + /* Y inclination coordinate */ + y = sin(torad(lPP - NP)) * cos(torad(minc)); + + /* X inclination coordinate */ + x = cos(torad(lPP - NP)); + + /* Ecliptic longitude */ + Lambdamoon = todeg(atan2(y, x)); + Lambdamoon += NP; + + /* Ecliptic latitude */ + BetaM = todeg(asin(sin(torad(lPP - NP)) * sin(torad(minc)))); + + /* Calculation of the phase of the Moon */ + + /* Age of the Moon in degrees */ + MoonAge = lPP - Lambdasun; + + /* Phase of the Moon */ + MoonPhase = (1 - cos(torad(MoonAge))) / 2; + + /* Calculate distance of moon from the centre of the Earth */ + + MoonDist = (msmax * (1 - mecc * mecc)) / + (1 + mecc * cos(torad(MmP + mEc))); + + /* Calculate Moon's angular diameter */ + + MoonDFrac = MoonDist / msmax; + MoonAng = mangsiz / MoonDFrac; + + /* Calculate Moon's parallax */ + + MoonPar = mparallax / MoonDFrac; + + *pphase = MoonPhase; + *mage = synmonth * (fixangle(MoonAge) / 360.0); + *dist = MoonDist; + *angdia = MoonAng; + *sudist = SunDist; + *suangdia = SunAng; + return fixangle(MoonAge) / 360.0; +} diff --git a/libs/phoon_14Aug2014/astro.h b/libs/phoon_14Aug2014/astro.h new file mode 100644 index 0000000..048e05d --- /dev/null +++ b/libs/phoon_14Aug2014/astro.h @@ -0,0 +1,46 @@ +#ifndef _ASTRO_H_ +#define _ASTRO_H_ + +/* + * UNIX_TO_JULIAN -- Convert internal Unix date/time to astronomical + * Julian time (i.e. Julian date plus day fraction, expressed as + * a double). + */ +double unix_to_julian( time_t t ); + +/* + * PHASEHUNT5 -- Find time of phases of the moon which surround + * the current date. Five phases are found, starting + * and ending with the new moons which bound the + * current lunation. + */ +void phasehunt5( double sdate, double phases[5] ); + +/* + * PHASEHUNT2 -- Find time of phases of the moon which surround + * the current date. Two phases are found. + */ +void phasehunt2( double sdate, double phases[2], double which[2] ); + +/* + * PHASE -- Calculate phase of moon as a fraction: + * + * The argument is the time for which the phase is requested, + * expressed as a Julian date and fraction. Returns the terminator + * phase angle as a percentage of a full circle (i.e., 0 to 1), + * and stores into pointer arguments the illuminated fraction of + * the Moon's disc, the Moon's age in days and fraction, the + * distance of the Moon from the centre of the Earth, and the + * angular diameter subtended by the Moon as seen by an observer + * at the centre of the Earth. + * + * pphase: Illuminated fraction + * mage: Age of moon in days + * dist: Distance in kilometres + * angdia: Angular diameter in degrees + * sudist: Distance to Sun + * suangdia: Sun's angular diameter + */ +double phase( double pdate, double* pphase, double* mage, double* dist, double* angdia, double* sudist, double* suangdia ); + +#endif /* _ASTRO_H_ */ diff --git a/libs/phoon_14Aug2014/date_parse.c b/libs/phoon_14Aug2014/date_parse.c new file mode 100644 index 0000000..7e600ff --- /dev/null +++ b/libs/phoon_14Aug2014/date_parse.c @@ -0,0 +1,1048 @@ +/* date_parse - parse string dates into internal form +** +** Copyright © 1995 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +/* For a while now I've been somewhat unhappy with the various +** date-parsing routines available - yacc-based, lex-based, ad-hoc. Large +** code size and not very portable are the main complaints. So I wrote a +** new one that just does a bunch of sscanf's until one matches. Slow, +** but small and portable. To figure out what formats to support I did a +** survey of Date: lines in a bunch of Usenet articles. The following +** two formats accounted for more than 99% of all articles: +** DD mth YY HH:MM:SS ampm zone +** wdy, DD mth YY HH:MM:SS ampm zone +** I added Unix ctime() format and a few others: +** wdy, mth DD HH:MM:SS ampm zone YY +** HH:MM:SS ampm zone DD mth YY +** DD mth YY +** HH:MM:SS ampm +** DD/mth/YYYY:HH:MM:SS zone (httpd common log format date) +** No-zone, no-seconds, and no-am/pm versions of each are also supported. +** Note that dd/mm/yy and mm/dd/yy are NOT supported - those formats are +** dumb. +*/ + +#include +#include +#include +#include +#include +#include + +#include "date_parse.h" + +#ifdef OS_SYSV +extern long timezone; +#endif /* OS_SYSV */ + +struct strlong { + char* s; + long l; + }; + +#define AMPM_NONE 0 +#define AMPM_AM 1 +#define AMPM_PM 2 + +static void pound_case( char* str ); +static int strlong_compare( const void* v1, const void* v2 ); +static int strlong_search( char* str, struct strlong* tab, int n, long* lP ); +static int ampm_fix( int hour, int ampm ); +static int scan_ampm( char* str_ampm, long* ampmP ); +static int scan_wday( char* str_wday, long* tm_wdayP ); +static int scan_mon( char* str_mon, long* tm_monP ); +static int scan_gmtoff( char* str_gmtoff, long* gmtoffP ); +static int is_leap( int year ); +static time_t tm_to_time( struct tm* tmP ); + + +time_t +date_parse( char* str ) + { + time_t now; + struct tm* now_tmP; + struct tm tm; + char* cp; + char str_mon[500], str_wday[500], str_gmtoff[500], str_ampm[500]; + int tm_sec, tm_min, tm_hour, tm_mday, tm_year; + long tm_mon, tm_wday, ampm, gmtoff; + int got_zone; + time_t t; + int i; + + /* If it's all digits, treat it as a raw Unix time and we're done. */ + t = strtol( str, &cp, 10 ); + if ( *cp == '\0' ) + return t; + + /* Initialize tm with relevant parts of current local time. */ + now = time( (time_t*) 0 ); + now_tmP = localtime( &now ); + + (void) memset( (char*) &tm, 0, sizeof(struct tm) ); + tm.tm_sec = now_tmP->tm_sec; + tm.tm_min = now_tmP->tm_min; + tm.tm_hour = now_tmP->tm_hour; + tm.tm_mday = now_tmP->tm_mday; + tm.tm_mon = now_tmP->tm_mon; + tm.tm_year = now_tmP->tm_year; + tm.tm_isdst = now_tmP->tm_isdst; + ampm = AMPM_NONE; + got_zone = 0; + + /* Find local zone offset. This is the only real area of + ** non-portability, and it's only used for local times that don't + ** specify a zone - those don't occur in email and netnews. + */ +#ifdef OS_SYSV + tzset(); + gmtoff = -timezone; +#else /* OS_SYSV */ + gmtoff = now_tmP->tm_gmtoff; +#endif /* OS_SYSV */ + + /* Skip initial whitespace ourselves - sscanf is clumsy at this. */ + for ( cp = str; *cp == ' ' || *cp == '\t'; ++cp ) + continue; + + /* And do the sscanfs. WARNING: you can add more formats here, + ** but be careful! You can easily screw up the parsing of existing + ** formats when you add new ones. The order is important. + */ + + /* DD/mth/YY:HH:MM:SS zone */ + if ( sscanf( cp, "%d/%400[a-zA-Z]/%d:%d:%d:%d %400[^: \n]", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, &tm_sec, + str_gmtoff ) == 7 && + scan_mon( str_mon, &tm_mon ) && + scan_gmtoff( str_gmtoff, &gmtoff ) ) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + got_zone = 1; + } + + /* DD-mth-YY HH:MM:SS ampm zone */ + else if ( ( ( sscanf( cp, "%d-%400[a-zA-Z]-%d %d:%d:%d %400[apmAPM] %400[^: \n]", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, &tm_sec, + str_ampm, str_gmtoff ) == 8 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d-%400[a-zA-Z]-%d %d:%d:%d %400[^: \n]", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, &tm_sec, + str_gmtoff ) == 7 ) && + scan_mon( str_mon, &tm_mon ) && + scan_gmtoff( str_gmtoff, &gmtoff ) ) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + got_zone = 1; + } + /* DD-mth-YY HH:MM ampm zone */ + else if ( ( ( sscanf( cp, "%d-%400[a-zA-Z]-%d %d:%d %400[apmAPM] %400[^: \n]", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, str_ampm, + str_gmtoff ) == 7 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d-%400[a-zA-Z]-%d %d:%d %400[^: \n]", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + str_gmtoff ) == 6 ) && + scan_mon( str_mon, &tm_mon ) && + scan_gmtoff( str_gmtoff, &gmtoff ) ) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = 0; + got_zone = 1; + } + /* DD-mth-YY HH:MM:SS ampm */ + else if ( ( ( sscanf( cp, "%d-%400[a-zA-Z]-%d %d:%d:%d %400[apmAPM]", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, &tm_sec, + str_ampm ) == 7 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d-%400[a-zA-Z]-%d %d:%d:%d", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec ) == 6 ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + /* DD-mth-YY HH:MM ampm */ + else if ( ( ( sscanf( cp, "%d-%400[a-zA-Z]-%d %d:%d %400[apmAPM]", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + str_ampm ) == 6 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d-%400[a-zA-Z]-%d %d:%d", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min ) == 5 ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = 0; + } + + /* DD mth YY HH:MM:SS ampm zone */ + else if ( ( ( sscanf( cp, "%d %400[a-zA-Z] %d %d:%d:%d %400[apmAPM] %400[^: \n]", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, &tm_sec, + str_ampm, str_gmtoff ) == 8 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d %400[a-zA-Z] %d %d:%d:%d %400[^: \n]", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, &tm_sec, + str_gmtoff ) == 7 ) && + scan_mon( str_mon, &tm_mon ) && + scan_gmtoff( str_gmtoff, &gmtoff ) ) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + got_zone = 1; + } + /* DD mth YY HH:MM ampm zone */ + else if ( ( ( sscanf( cp, "%d %400[a-zA-Z] %d %d:%d %400[apmAPM] %400[^: \n]", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, str_ampm, + str_gmtoff ) == 7 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d %400[a-zA-Z] %d %d:%d %400[^: \n]", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + str_gmtoff ) == 6 ) && + scan_mon( str_mon, &tm_mon ) && + scan_gmtoff( str_gmtoff, &gmtoff ) ) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = 0; + got_zone = 1; + } + /* DD mth YY HH:MM:SS ampm */ + else if ( ( ( sscanf( cp, "%d %400[a-zA-Z] %d %d:%d:%d %400[apmAPM]", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, &tm_sec, + str_ampm ) == 7 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d %400[a-zA-Z] %d %d:%d:%d", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec ) == 6 ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + /* DD mth YY HH:MM ampm */ + else if ( ( ( sscanf( cp, "%d %400[a-zA-Z] %d %d:%d %400[apmAPM]", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + str_ampm ) == 6 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d %400[a-zA-Z] %d %d:%d", + &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min ) == 5 ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = 0; + } + + /* DD mth HH:MM:SS ampm */ + else if ( ( ( sscanf( cp, "%d %400[a-zA-Z] %d:%d:%d %400[apmAPM]", + &tm_mday, str_mon, &tm_hour, &tm_min, &tm_sec, + str_ampm ) == 6 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d %400[a-zA-Z] %d:%d:%d", + &tm_mday, str_mon, &tm_hour, &tm_min, + &tm_sec ) == 5 ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + /* DD mth HH:MM ampm */ + else if ( ( ( sscanf( cp, "%d %400[a-zA-Z] %d:%d %400[apmAPM]", + &tm_mday, str_mon, &tm_hour, &tm_min, + str_ampm ) == 5 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d %400[a-zA-Z] %d:%d", + &tm_mday, str_mon, &tm_hour, &tm_min ) == 4 ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = 0; + } + + /* HH:MM:SS ampm zone DD-mth-YY */ + else if ( ( ( sscanf( cp, "%d:%d:%d %400[apmAPM] %400[^: \n] %d-%400[a-zA-Z]-%d", + &tm_hour, &tm_min, &tm_sec, str_ampm, str_gmtoff, &tm_mday, + str_mon, &tm_year ) == 8 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d:%d:%d %400[^: \n] %d-%400[a-zA-Z]-%d", + &tm_hour, &tm_min, &tm_sec, str_gmtoff, &tm_mday, str_mon, + &tm_year ) == 7 ) && + scan_gmtoff( str_gmtoff, &gmtoff ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + } + /* HH:MM ampm zone DD-mth-YY */ + else if ( ( ( sscanf( cp, "%d:%d %400[apmAPM] %400[^: \n] %d-%400[a-zA-Z]-%d", + &tm_hour, &tm_min, str_ampm, str_gmtoff, &tm_mday, str_mon, + &tm_year ) == 7 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d:%d %400[^: \n] %d-%400[a-zA-Z]-%d", + &tm_hour, &tm_min, str_gmtoff, &tm_mday, str_mon, + &tm_year ) == 6 ) && + scan_gmtoff( str_gmtoff, &gmtoff ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = 0; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + } + /* HH:MM:SS ampm DD-mth-YY */ + else if ( ( ( sscanf( cp, "%d:%d:%d %400[apmAPM] %d-%400[a-zA-Z]-%d", + &tm_hour, &tm_min, &tm_sec, str_ampm, &tm_mday, str_mon, + &tm_year ) == 7 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d:%d:%d %d-%400[a-zA-Z]-%d", + &tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon, + &tm_year ) == 6 ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + } + /* HH:MM ampm DD-mth-YY */ + else if ( ( ( sscanf( cp, "%d:%d %400[apmAPM] %d-%400[a-zA-Z]-%d", + &tm_hour, &tm_min, str_ampm, &tm_mday, str_mon, + &tm_year ) == 6 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d:%d %d-%400[a-zA-Z]-%d", + &tm_hour, &tm_min, &tm_mday, str_mon, &tm_year ) == 5 ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = 0; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + } + + /* HH:MM:SS ampm zone DD mth YY */ + else if ( ( ( sscanf( cp, "%d:%d:%d %400[apmAPM] %400[^: \n] %d %400[a-zA-Z] %d", + &tm_hour, &tm_min, &tm_sec, str_ampm, str_gmtoff, &tm_mday, + str_mon, &tm_year ) == 8 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d:%d:%d %400[^: \n] %d %400[a-zA-Z] %d", + &tm_hour, &tm_min, &tm_sec, str_gmtoff, &tm_mday, str_mon, + &tm_year ) == 7 ) && + scan_gmtoff( str_gmtoff, &gmtoff ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + } + /* HH:MM ampm zone DD mth YY */ + else if ( ( ( sscanf( cp, "%d:%d %400[apmAPM] %400[^: \n] %d %400[a-zA-Z] %d", + &tm_hour, &tm_min, str_ampm, str_gmtoff, &tm_mday, str_mon, + &tm_year ) == 7 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d:%d %400[^: \n] %d %400[a-zA-Z] %d", + &tm_hour, &tm_min, str_gmtoff, &tm_mday, str_mon, + &tm_year ) == 6 ) && + scan_gmtoff( str_gmtoff, &gmtoff ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = 0; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + } + /* HH:MM:SS ampm DD mth YY */ + else if ( ( ( sscanf( cp, "%d:%d:%d %400[apmAPM] %d %400[a-zA-Z] %d", + &tm_hour, &tm_min, &tm_sec, str_ampm, &tm_mday, str_mon, + &tm_year ) == 7 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d:%d:%d %d %400[a-zA-Z] %d", + &tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon, + &tm_year ) == 6 ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + } + /* HH:MM ampm DD mth YY */ + else if ( ( ( sscanf( cp, "%d:%d %400[apmAPM] %d %400[a-zA-Z] %d", + &tm_hour, &tm_min, str_ampm, &tm_mday, str_mon, + &tm_year ) == 6 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d:%d %d %400[a-zA-Z] %d", + &tm_hour, &tm_min, &tm_mday, str_mon, &tm_year ) == 5 ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = 0; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + } + + /* wdy, DD-mth-YY HH:MM:SS ampm zone */ + else if ( ( ( sscanf( cp, "%400[a-zA-Z,] %d-%400[a-zA-Z]-%d %d:%d:%d %400[apmAPM] %400[^: \n]", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec, str_ampm, str_gmtoff ) == 9 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%400[a-zA-Z], %d-%400[a-zA-Z]-%d %d:%d:%d %400[^: \n]", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec, str_gmtoff ) == 8 ) && + scan_wday( str_wday, &tm_wday ) && + scan_mon( str_mon, &tm_mon ) && + scan_gmtoff( str_gmtoff, &gmtoff ) ) + { + tm.tm_wday = tm_wday; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + got_zone = 1; + } + /* wdy, DD-mth-YY HH:MM ampm zone */ + else if ( ( ( sscanf( cp, "%400[a-zA-Z,] %d-%400[a-zA-Z]-%d %d:%d %400[apmAPM] %400[^: \n]", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + str_ampm, str_gmtoff ) == 8 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%400[a-zA-Z], %d-%400[a-zA-Z]-%d %d:%d %400[^: \n]", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + str_gmtoff ) == 7 ) && + scan_wday( str_wday, &tm_wday ) && + scan_mon( str_mon, &tm_mon ) && + scan_gmtoff( str_gmtoff, &gmtoff ) ) + { + tm.tm_wday = tm_wday; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = 0; + got_zone = 1; + } + /* wdy, DD-mth-YY HH:MM:SS ampm */ + else if ( ( ( sscanf( cp, "%400[a-zA-Z,] %d-%400[a-zA-Z]-%d %d:%d:%d %400[apmAPM]", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec, str_ampm ) == 8 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%400[a-zA-Z], %d-%400[a-zA-Z]-%d %d:%d:%d", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec ) == 7 ) && + scan_wday( str_wday, &tm_wday ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_wday = tm_wday; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + /* wdy, DD-mth-YY HH:MM ampm */ + else if ( ( ( sscanf( cp, "%400[a-zA-Z,] %d-%400[a-zA-Z]-%d %d:%d %400[apmAPM]", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + str_ampm ) == 7 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%400[a-zA-Z], %d-%400[a-zA-Z]-%d %d:%d", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, + &tm_min ) == 6 ) && + scan_wday( str_wday, &tm_wday ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_wday = tm_wday; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = 0; + } + + /* wdy, DD mth YY HH:MM:SS ampm zone */ + else if ( ( ( sscanf( cp, "%400[a-zA-Z,] %d %400[a-zA-Z] %d %d:%d:%d %400[apmAPM] %400[^: \n]", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec, str_ampm, str_gmtoff ) == 9 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%400[a-zA-Z], %d %400[a-zA-Z] %d %d:%d:%d %400[^: \n]", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec, str_gmtoff ) == 8 ) && + scan_wday( str_wday, &tm_wday ) && + scan_mon( str_mon, &tm_mon ) && + scan_gmtoff( str_gmtoff, &gmtoff ) ) + { + tm.tm_wday = tm_wday; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + got_zone = 1; + } + /* wdy, DD mth YY HH:MM ampm zone */ + else if ( ( ( sscanf( cp, "%400[a-zA-Z,] %d %400[a-zA-Z] %d %d:%d %400[apmAPM] %400[^: \n]", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + str_ampm, str_gmtoff ) == 8 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%400[a-zA-Z], %d %400[a-zA-Z] %d %d:%d %400[^: \n]", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + str_gmtoff ) == 7 ) && + scan_wday( str_wday, &tm_wday ) && + scan_mon( str_mon, &tm_mon ) && + scan_gmtoff( str_gmtoff, &gmtoff ) ) + { + tm.tm_wday = tm_wday; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = 0; + got_zone = 1; + } + /* wdy, DD mth YY HH:MM:SS ampm */ + else if ( ( ( sscanf( cp, "%400[a-zA-Z,] %d %400[a-zA-Z] %d %d:%d:%d %400[apmAPM]", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec, str_ampm ) == 8 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%400[a-zA-Z], %d %400[a-zA-Z] %d %d:%d:%d", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + &tm_sec ) == 7 ) && + scan_wday( str_wday, &tm_wday ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_wday = tm_wday; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + /* wdy, DD mth YY HH:MM ampm */ + else if ( ( ( sscanf( cp, "%400[a-zA-Z,] %d %400[a-zA-Z] %d %d:%d %400[apmAPM]", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min, + str_ampm ) == 7 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%400[a-zA-Z], %d %400[a-zA-Z] %d %d:%d", + str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, + &tm_min ) == 6 ) && + scan_wday( str_wday, &tm_wday ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_wday = tm_wday; + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = 0; + } + + /* wdy, mth DD HH:MM:SS ampm zone YY */ + else if ( ( ( sscanf( cp, "%400[a-zA-Z,] %400[a-zA-Z] %d %d:%d:%d %400[apmAPM] %400[^: \n] %d", + str_wday, str_mon, &tm_mday, &tm_hour, &tm_min, &tm_sec, + str_ampm, str_gmtoff, &tm_year ) == 9 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%400[a-zA-Z] %400[a-zA-Z] %d %d:%d:%d %400[^: \n] %d", + str_wday, str_mon, &tm_mday, &tm_hour, &tm_min, &tm_sec, + str_gmtoff, &tm_year ) == 8 ) && + scan_wday( str_wday, &tm_wday ) && + scan_mon( str_mon, &tm_mon ) && + scan_gmtoff( str_gmtoff, &gmtoff ) ) + { + tm.tm_wday = tm_wday; + tm.tm_mon = tm_mon; + tm.tm_mday = tm_mday; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + got_zone = 1; + tm.tm_year = tm_year; + } + /* wdy, mth DD HH:MM ampm zone YY */ + else if ( ( ( sscanf( cp, "%400[a-zA-Z,] %400[a-zA-Z] %d %d:%d %400[apmAPM] %400[^: \n] %d", + str_wday, str_mon, &tm_mday, &tm_hour, &tm_min, + str_ampm, str_gmtoff, &tm_year ) == 8 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%400[a-zA-Z] %400[a-zA-Z] %d %d:%d %400[^: \n] %d", + str_wday, str_mon, &tm_mday, &tm_hour, &tm_min, + str_gmtoff, &tm_year ) == 7 ) && + scan_wday( str_wday, &tm_wday ) && + scan_mon( str_mon, &tm_mon ) && + scan_gmtoff( str_gmtoff, &gmtoff ) ) + { + tm.tm_wday = tm_wday; + tm.tm_mon = tm_mon; + tm.tm_mday = tm_mday; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = 0; + got_zone = 1; + tm.tm_year = tm_year; + } + /* wdy, mth DD HH:MM:SS ampm YY */ + else if ( ( ( sscanf( cp, "%400[a-zA-Z,] %400[a-zA-Z] %d %d:%d:%d %400[apmAPM] %d", + str_wday, str_mon, &tm_mday, &tm_hour, &tm_min, &tm_sec, + str_ampm, &tm_year ) == 8 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%400[a-zA-Z] %400[a-zA-Z] %d %d:%d:%d %d", + str_wday, str_mon, &tm_mday, &tm_hour, &tm_min, &tm_sec, + &tm_year ) == 7 ) && + scan_wday( str_wday, &tm_wday ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_wday = tm_wday; + tm.tm_mon = tm_mon; + tm.tm_mday = tm_mday; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + tm.tm_year = tm_year; + } + /* wdy, mth DD HH:MM ampm YY */ + else if ( ( ( sscanf( cp, "%400[a-zA-Z,] %400[a-zA-Z] %d %d:%d %400[apmAPM] %d", + str_wday, str_mon, &tm_mday, &tm_hour, &tm_min, + str_ampm, &tm_year ) == 7 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%400[a-zA-Z] %400[a-zA-Z] %d %d:%d %d", + str_wday, str_mon, &tm_mday, &tm_hour, &tm_min, + &tm_year ) == 6 ) && + scan_wday( str_wday, &tm_wday ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_wday = tm_wday; + tm.tm_mon = tm_mon; + tm.tm_mday = tm_mday; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = 0; + tm.tm_year = tm_year; + } + + /* mth DD HH:MM:SS ampm */ + else if ( ( ( sscanf( cp, "%400[a-zA-Z] %d %d:%d:%d %400[apmAPM]", + str_mon, &tm_mday, &tm_hour, &tm_min, &tm_sec, + str_ampm ) == 6 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%400[a-zA-Z] %d %d:%d:%d", + str_mon, &tm_mday, &tm_hour, &tm_min, + &tm_sec ) == 5 ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_mon = tm_mon; + tm.tm_mday = tm_mday; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + /* mth DD HH:MM ampm */ + else if ( ( ( sscanf( cp, "%400[a-zA-Z] %d %d:%d %400[apmAPM]", + str_mon, &tm_mday, &tm_hour, &tm_min, + str_ampm ) == 5 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%400[a-zA-Z] %d %d:%d", + str_mon, &tm_mday, &tm_hour, &tm_min ) == 4 ) && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_mon = tm_mon; + tm.tm_mday = tm_mday; + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = 0; + } + + /* DD-mth-YY */ + else if ( sscanf( cp, "%d-%400[a-zA-Z]-%d", + &tm_mday, str_mon, &tm_year ) == 3 && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = 0; + tm.tm_min = 0; + tm.tm_sec = 0; + } + /* DD mth YY */ + else if ( sscanf( cp, "%d %400[a-zA-Z] %d", + &tm_mday, str_mon, &tm_year ) == 3 && + scan_mon( str_mon, &tm_mon ) ) + { + tm.tm_mday = tm_mday; + tm.tm_mon = tm_mon; + tm.tm_year = tm_year; + tm.tm_hour = 0; + tm.tm_min = 0; + tm.tm_sec = 0; + } + + /* HH:MM:SS ampm */ + else if ( ( sscanf( cp, "%d:%d:%d %400[apmAPM]", + &tm_hour, &tm_min, &tm_sec, str_ampm ) == 4 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d:%d:%d", &tm_hour, &tm_min, &tm_sec ) == 3 ) + { + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = tm_sec; + } + /* HH:MM ampm */ + else if ( ( sscanf( cp, "%d:%d %400[apmAPM]", &tm_hour, &tm_min, + str_ampm ) == 3 && + scan_ampm( str_ampm, &m ) ) || + sscanf( cp, "%d:%d", &tm_hour, &tm_min ) == 2 ) + { + tm.tm_hour = ampm_fix( tm_hour, ampm ); + tm.tm_min = tm_min; + tm.tm_sec = 0; + } + else if ( sscanf( cp, "%d", &i ) == 1 && + i >= 300000000 && i <= 2000000000 ) + /* ~1980 ~2033 */ + { + t = (time_t) i; + return t; + } + else + return (time_t) -1; + + if ( tm.tm_year > 1900 ) + tm.tm_year -= 1900; + else if ( tm.tm_year < 70 ) + tm.tm_year += 100; + + t = tm_to_time( &tm ); + t -= gmtoff; +#ifdef notdef + /* This doesn't seem to be necessary, at least on SunOS. */ + if ( tm.tm_isdst && ! got_zone ) + t -= 60*60; +#endif + + return t; + } + + +static void +pound_case( char* str ) + { + for ( ; *str != '\0'; ++str ) + { + if ( isupper( (int) *str ) ) + *str = tolower( (int) *str ); + } + } + + +static int +strlong_compare( const void* v1, const void* v2 ) + { + const struct strlong* s1 = (const struct strlong*) v1; + const struct strlong* s2 = (const struct strlong*) v2; + + return strcmp( s1->s, s2->s ); + } + + +static int +strlong_search( char* str, struct strlong* tab, int n, long* lP ) + { + int i, h, l, r; + + l = 0; + h = n - 1; + for (;;) + { + i = ( h + l ) / 2; + r = strcmp( str, tab[i].s ); + if ( r < 0 ) + h = i - 1; + else if ( r > 0 ) + l = i + 1; + else + { + *lP = tab[i].l; + return 1; + } + if ( h < l ) + return 0; + } + } + + +static int +ampm_fix( int hour, int ampm ) + { + switch ( ampm ) + { + case AMPM_NONE: + break; + case AMPM_AM: + if ( hour == 12 ) + hour = 0; + break; + case AMPM_PM: + if ( hour != 12 ) + hour += 12; + break; + } + return hour; + } + + +static int +scan_ampm( char* str_ampm, long* ampmP ) + { + static struct strlong ampm_tab[] = { + { "am", AMPM_AM }, { "pm", AMPM_PM }, + }; + static int sorted = 0; + + if ( ! sorted ) + { + (void) qsort( + ampm_tab, sizeof(ampm_tab)/sizeof(struct strlong), + sizeof(struct strlong), strlong_compare ); + sorted = 1; + } + pound_case( str_ampm ); + return strlong_search( + str_ampm, ampm_tab, sizeof(ampm_tab)/sizeof(struct strlong), ampmP ); + } + + +static int +scan_wday( char* str_wday, long* tm_wdayP ) + { + static struct strlong wday_tab[] = { + { "sun", 0 }, { "sunday", 0 }, + { "mon", 1 }, { "monday", 1 }, + { "tue", 2 }, { "tuesday", 2 }, + { "wed", 3 }, { "wednesday", 3 }, + { "thu", 4 }, { "thursday", 4 }, + { "fri", 5 }, { "friday", 5 }, + { "sat", 6 }, { "saturday", 6 }, + }; + static int sorted = 0; + + if ( ! sorted ) + { + (void) qsort( + wday_tab, sizeof(wday_tab)/sizeof(struct strlong), + sizeof(struct strlong), strlong_compare ); + sorted = 1; + } + pound_case( str_wday ); + return strlong_search( + str_wday, wday_tab, sizeof(wday_tab)/sizeof(struct strlong), tm_wdayP ); + } + + +static int +scan_mon( char* str_mon, long* tm_monP ) + { + static struct strlong mon_tab[] = { + { "jan", 0 }, { "january", 0 }, + { "feb", 1 }, { "february", 1 }, + { "mar", 2 }, { "march", 2 }, + { "apr", 3 }, { "april", 3 }, + { "may", 4 }, + { "jun", 5 }, { "june", 5 }, + { "jul", 6 }, { "july", 6 }, + { "aug", 7 }, { "august", 7 }, + { "sep", 8 }, { "september", 8 }, + { "oct", 9 }, { "october", 9 }, + { "nov", 10 }, { "november", 10 }, + { "dec", 11 }, { "december", 11 }, + }; + static int sorted = 0; + + if ( ! sorted ) + { + (void) qsort( + mon_tab, sizeof(mon_tab)/sizeof(struct strlong), + sizeof(struct strlong), strlong_compare ); + sorted = 1; + } + pound_case( str_mon ); + return strlong_search( + str_mon, mon_tab, sizeof(mon_tab)/sizeof(struct strlong), tm_monP ); + } + + +static int +scan_gmtoff( char* str_gmtoff, long* gmtoffP ) + { + static struct strlong gmtoff_tab[] = { + { "gmt", 0L }, { "utc", 0L }, { "ut", 0L }, + { "0000", 0L }, { "+0000", 0L }, { "-0000", 0L }, + { "0100", 3600L }, { "+0100", 3600L }, { "-0100", -3600L }, + { "0200", 7200L }, { "+0200", 7200L }, { "-0200", -7200L }, + { "0300", 10800L }, { "+0300", 10800L }, { "-0300", -10800L }, + { "0400", 14400L }, { "+0400", 14400L }, { "-0400", -14400L }, + { "0500", 18000L }, { "+0500", 18000L }, { "-0500", -18000L }, + { "0600", 21600L }, { "+0600", 21600L }, { "-0600", -21600L }, + { "0700", 25200L }, { "+0700", 25200L }, { "-0700", -25200L }, + { "0800", 28800L }, { "+0800", 28800L }, { "-0800", -28800L }, + { "0900", 32400L }, { "+0900", 32400L }, { "-0900", -32400L }, + { "1000", 36000L }, { "+1000", 36000L }, { "-1000", -36000L }, + { "1100", 39600L }, { "+1100", 39600L }, { "-1100", -39600L }, + { "1200", 43200L }, { "+1200", 43200L }, { "-1200", -43200L }, + { "cet", 3600L }, { "ced", 7200L }, /* Central European time */ + { "mez", 3600L }, { "mesz", 7200L }, /* Mitteleuropdische Zeit */ + { "jst", 32400L }, { "jdt", 36000L }, /* Japan time */ + { "bst", -3600L }, + { "nst", -12600L }, + { "ast", -14400L }, { "adt", -10800L }, + { "est", -18000L }, { "edt", -14400L }, + { "cst", -21600L }, { "cdt", -18000L }, + { "mst", -25200L }, { "mdt", -21600L }, + { "pst", -28800L }, { "pdt", -25200L }, + { "yst", -32400L }, { "ydt", -28800L }, + { "hst", -36000L }, { "hdt", -32400L }, + { "a", -3600L }, { "b", -7200L }, { "c", -10800L }, { "d", -14400L }, + { "e", -18000L }, { "f", -21600L }, { "g", -25200L }, { "h", -28800L }, + { "i", -32400L }, { "k", -36000L }, { "l", -39600L }, { "m", -43200L }, + { "n", 3600L }, { "o", 7200L }, { "p", 10800L }, { "q", 14400L }, + { "r", 18000L }, { "s", 21600L }, { "t", 25200L }, { "u", 28800L }, + { "v", 32400L }, { "w", 36000L }, { "x", 39600L }, { "y", 43200L }, + { "z", 0L }, + }; + static int sorted = 0; + + if ( ! sorted ) + { + (void) qsort( + gmtoff_tab, sizeof(gmtoff_tab)/sizeof(struct strlong), + sizeof(struct strlong), strlong_compare ); + sorted = 1; + } + pound_case( str_gmtoff ); + return strlong_search( + str_gmtoff, gmtoff_tab, sizeof(gmtoff_tab)/sizeof(struct strlong), + gmtoffP ); + } + + +static int +is_leap( int year ) + { + return year % 400? ( year % 100 ? ( year % 4 ? 0 : 1 ) : 0 ) : 1; + } + + +/* Basically the same as mktime(). */ +static time_t +tm_to_time( struct tm* tmP ) + { + time_t t; + static int monthtab[12] = { + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + + /* Years since epoch, converted to days. */ + t = ( tmP->tm_year - 70 ) * 365; + /* Leap days for previous years - this will break in 2100! */ + t += ( tmP->tm_year - 69 ) / 4; + /* Days for the beginning of this month. */ + t += monthtab[tmP->tm_mon]; + /* Leap day for this year. */ + if ( tmP->tm_mon >= 2 && is_leap( tmP->tm_year + 1900 ) ) + ++t; + /* Days since the beginning of this month. */ + t += tmP->tm_mday - 1; /* 1-based field */ + /* Hours, minutes, and seconds. */ + t = t * 24 + tmP->tm_hour; + t = t * 60 + tmP->tm_min; + t = t * 60 + tmP->tm_sec; + + return t; + } diff --git a/libs/phoon_14Aug2014/date_parse.h b/libs/phoon_14Aug2014/date_parse.h new file mode 100644 index 0000000..165fde9 --- /dev/null +++ b/libs/phoon_14Aug2014/date_parse.h @@ -0,0 +1,33 @@ +/* date_parse.h - parse string dates into internal form +** +** Copyright © 1995 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _DATE_PARSE_H_ +#define _DATE_PARSE_H_ + +time_t date_parse( char* str ); + +#endif /* _DATE_PARSE_H_ */ diff --git a/libs/phoon_14Aug2014/phoon.1 b/libs/phoon_14Aug2014/phoon.1 new file mode 100644 index 0000000..525b02c --- /dev/null +++ b/libs/phoon_14Aug2014/phoon.1 @@ -0,0 +1,23 @@ +.TH phoon 1 "07 June 1988" +.SH NAME +phoon - show the PHase of the mOON +.SH SYNOPSIS +phoon +.RB -l +.IR lines ] +.RI [ date ] +.SH DESCRIPTION +.I Phoon +displays the phase of the moon, either currently +or at a specified date / time. +Unlike other such programs, which just tell you how long since first quarter +or something like that, phoon +.I shows +you the phase with a cute little picture. +You can vary the size of the picture with the -l flag, but only some +sizes have pictures defined - other sizes use @'s. +.SH "SEE ALSO" +date_parse(3) +.SH AUTHOR +Jef Poskanzer. +The moon-phase computation is from "moontool.c", by John Walker. diff --git a/libs/phoon_14Aug2014/phoon.c b/libs/phoon_14Aug2014/phoon.c new file mode 100644 index 0000000..c9ea040 --- /dev/null +++ b/libs/phoon_14Aug2014/phoon.c @@ -0,0 +1,575 @@ +/* phoon - show the phase of the moon +** +** ver date who remarks +** --- ------- --- ------------------------------------------------------------- +** 03A 01apr95 JP Updated to use date_parse. +** 02A 07jun88 JP Changed the phase calculation to use code from John Walker's +** "moontool", increasing accuracy tremendously. +** Got rid of SINFORCOS, since Apple has probably fixed A/UX +** by now. +** 01I 03feb88 JP Added 32 lines. +** 01H 14jan88 JP Added 22 lines. +** 01G 05dec87 JP Added random sabotage to generate Hubert. +** Defeated text stuff for moons 28 or larger. +** 01F 13oct87 JP Added pumpkin19 in October. Added hubert29. +** 01E 14may87 JP Added #ifdef SINFORCOS to handle broken A/UX library. +** 01D 02apr87 JP Added 21 lines. +** 01C 26jan87 JP Added backgrounds for 29 and 18 lines. +** 01B 28dec86 JP Added -l flag, and backgrounds for 19 and 24 lines. +** 01A 08nov86 JP Translated from the ratfor version of 12nov85, which itself +** was translated from the Pascal version of 05apr79. +** +** Copyright (C) 1986,1987,1988,1995 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + + +#include +#include +#include +#include +#include + +#include "date_parse.h" +#include "astro.h" + + +/* Global defines and declarations. */ + +#define SECSPERMINUTE 60 +#define SECSPERHOUR (60 * SECSPERMINUTE) +#define SECSPERDAY (24 * SECSPERHOUR) + +#define PI 3.1415926535897932384626433 + +#define DEFAULTNUMLINES 23 + +#define QUARTERLITLEN 16 +#define QUARTERLITLENPLUSONE 17 + +/* If you change the aspect ratio, the canned backgrounds won't work. */ +#define ASPECTRATIO 0.5 + + +static void +putseconds( long secs ) + { + long days, hours, minutes; + + days = secs / SECSPERDAY; + secs = secs - days * SECSPERDAY; + hours = secs / SECSPERHOUR; + secs = secs - hours * SECSPERHOUR; + minutes = secs / SECSPERMINUTE; + secs = secs - minutes * SECSPERMINUTE; + + printf( "%ld %2ld:%02ld:%02ld", days, hours, minutes, secs ); + } + + +static void +putmoon( time_t t, int numlines, char* atfiller ) + { + static char background18[18][37] = { + " .----------. ", + " .--' o . `--. ", + " .'@ @@@@@@ O . . `. ", + " .'@@ @@@@@@@@ @@@@ . `. ", + " .' . @@@@@@@@ @@@@@@ . `. ", + " / @@ o @@@@@@. @@@@ O @\\ ", + " |@@@@ @@@@@@ @@| ", + " / @@@@@ `.-. . @@@@@@@@ . @@\\", + " | @@@@ --`-' . o @@@@@@@ |", + " |@ @@ @@@@@@ @@@ |", + " \\ @@ @ . () @@ @@@@@ /", + " | @ @@@ @@@ @@@ | ", + " \\ . @@ @\\ . . @@ o / ", + " `. @@@@ _\\ / . o .' ", + " `. @@ ()--- .' ", + " `. / | . o .' ", + " `--./ . .--' ", + " `----------' "}; + static char background19[19][39] = { + " .----------. ", + " .--' o . `--. ", + " .-'@ @@@@@@ O . . `-. ", + " .' @@ @@@@@@@@ @@@@ . `. ", + " / . @@@@@@@@ @@@@@@ . \\ ", + " /@@ o @@@@@@. @@@@ O @\\ ", + " /@@@@ @@@@@@ @@@\\ ", + " . @@@@@ `.-./ . @@@@@@@@ . @@ .", + " | @@@@ --`-' . @@@@@@@ |", + " |@ @@ ` o @@@@@@ @@@@ |", + " | @@ o @@ @@@@@@ |", + " ` . @ @@ () @@@ @@@@ '", + " \\ @@ @@@@ . @@ . o / ", + " \\ @@@@ @@\\ . o / ", + " \\ . @@ _\\ / . .-. / ", + " `. . ()--- `-' .' ", + " `-. ./ | . o .-' ", + " `--./ . .--' ", + " `----------' "}; + static char pumpkin19[19][39] = { + " @@@@@@@@@@@@ ", + " @@@@@@@@@@@@@@@@@@@@ ", + " @@@@@@@@@@@@@@@@@@@@@@@@@@ ", + " @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ", + " @@@@ @@@@@@@@ @@@@ ", + " @@@@@@ @@@@@@@@@@ @@@@@@ ", + " @@@@@@@@ @@@@@@@@@@@@ @@@@@@@@ ", + " @@@@@@@@@@ @@@@@@ @@@@@@ @@@@@@@@@@", + " @@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@", + " @@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@", + " @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@", + " @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", + " @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ", + " @@@@@ @@ @@@@@ ", + " @@@@@@ @@@@@@ ", + " @@@@@@@@ @@@@@@@@ ", + " @@@@@@@@@ @@@@@@@@@ ", + " @@@@@@@@@@@@@@@@@@@@ ", + " @@@@@@@@@@@@ "}; + static char background21[21][43] = { + " .----------. ", + " .---' O . . `---. ", + " .-'@ @@@@@@ . @@@@@ `-. ", + " .'@@ @@@@@@@@@ @@@@@@@ . `. ", + " / o @@@@@@@@@ @@@@@@@ . \\ ", + " /@ o @@@@@@@@@. @@@@@@@ O \\ ", + " /@@@ . @@@@@@o @@@@@@@@@@ @@\\ ", + " /@@@@@ . @@@@@@@@@@@@@ o @@@\\ ", + " .@@@@@ O `.-./ . @@@@@@@@@@@@ @@ .", + " | @@@@ --`-' o @@@@@@@@ @@@@ |", + " |@ @@@ ` o . @@ . @@@@@@@ |", + " | @@ @ .-. @@@ @@@@@@@ |", + " ` . @ @@@ `-' . @@@@ @@@@ o '", + " \\ @@ @@@@@ . @@ . / ", + " \\ @@@@ @\\@@ / . O . o . / ", + " \\o @@ \\ \\ / . . / ", + " \\ . .\\.-.___ . . .-. / ", + " `. `-' `-'.' ", + " `-. o / | o O . .-' ", + " `---. . . .---' ", + " `----------' "}; + static char background22[22][45] = { + " .------------. ", + " .--' o . . `--. ", + " .-' . O . . `-. ", + " .'@ @@@@@@@ . @@@@@ `. ", + " .'@@@ @@@@@@@@@@@ @@@@@@@ . `. ", + " / o @@@@@@@@@@@ @@@@@@@ . \\ ", + " /@@ o @@@@@@@@@@@. @@@@@@@ O \\ ", + " /@@@@ . @@@@@@@o @@@@@@@@@@ @@@\\ ", + " |@@@@@ . @@@@@@@@@@@@ @@@@| ", + " /@@@@@ O `.-./ . @@@@@@@@@@@ @@ \\", + " | @@@@ --`-' o . @@@@@@@ @@@@ |", + " |@ @@@ @@ @ ` o .-. @@ . @@@@@@ |", + " \\ @@@ `-' . @@@ @@@@@@ /", + " | . @ @@ @@@@@ . @@@@ @@@ o | ", + " \\ @@@@ @\\@@ / . O @@ . . / ", + " \\ o @@ \\ \\ / . . o / ", + " \\ . .\\.-.___ . . . .-. / ", + " `. `-' `-' .' ", + " `. o / | o O . .' ", + " `-. / . . .-' ", + " `--. . .--' ", + " `------------' "}; + static char background23[23][47] = { + " .------------. ", + " .--' o . . `--. ", + " .-' . O . . `-. ", + " .-'@ @@@@@@@ . @@@@@ `-. ", + " /@@@ @@@@@@@@@@@ @@@@@@@ . \\ ", + " ./ o @@@@@@@@@@@ @@@@@@@ . \\. ", + " /@@ o @@@@@@@@@@@. @@@@@@@ O \\ ", + " /@@@@ . @@@@@@@o @@@@@@@@@@ @@@ \\ ", + " |@@@@@ . @@@@@@@@@@@@@ o @@@@| ", + " /@@@@@ O `.-./ . @@@@@@@@@@@@ @@ \\", + " | @@@@ --`-' o @@@@@@@@ @@@@ |", + " |@ @@@ ` o . @@ . @@@@@@@ |", + " | @@ @ .-. @@@ @@@@@@@ |", + " \\ . @ @@@ `-' . @@@@ @@@@ o /", + " | @@ @@@@@ . @@ . | ", + " \\ @@@@ @\\@@ / . O . o . / ", + " \\ o @@ \\ \\ / . . / ", + " `\\ . .\\.-.___ . . .-. /' ", + " \\ `-' `-' / ", + " `-. o / | o O . .-' ", + " `-. / . . .-' ", + " `--. . .--' ", + " `------------' "}; + + static char background24[24][49] = { + " .------------. ", + " .---' o . . `---. ", + " .-' . O . . `-. ", + " .'@ @@@@@@@ . @@@@@ `. ", + " .'@@ @@@@@@@@@@@ @@@@@@@ . `. ", + " / o @@@@@@@@@@@ @@@@@@@ . \\ ", + " /@ o @@@@@@@@@@@. @@@@@@@ O \\ ", + " /@@@ . @@@@@@@o @@@@@@@@@@ @@@ \\ ", + " /@@@@@ . @@@@@@@@@@@@@ o @@@@ \\ ", + " |@@@@ O `.-./ . @@@@@@@@@@@@ @@ | ", + " / @@@@ --`-' o @@@@@@@@ @@@@ \\", + " |@ @@@ @ ` . @@ @@@@@@@ |", + " | @ o @ @@@@@@@ |", + " \\ @@ .-. @@@ @@@@ o /", + " | . @ @@@ `-' . @@@@ | ", + " \\ @@ @@@@@ . @@ . / ", + " \\ @@@@ @\\@@ / . O . o . / ", + " \\ o @@ \\ \\ / . . / ", + " \\ . .\\.-.___ . . .-. / ", + " `. `-' `-' .' ", + " `. o / | o O . .' ", + " `-. / . . .-' ", + " `---. . .---' ", + " `------------' "}; + static char background29[29][59] = { + " .--------------. ", + " .---' o . `---. ", + " .-' . O . . `-. ", + " .-' @@@@@@ . `-. ", + " .'@@ @@@@@@@@@@@ @@@@@@@ . `. ", + " .'@@@ @@@@@@@@@@@@@@ @@@@@@@@@ `. ", + " /@@@ o @@@@@@@@@@@@@@ @@@@@@@@@ O \\ ", + " / @@@@@@@@@@@@@@ @ @@@@@@@@@ @@ . \\ ", + " /@ o @@@@@@@@@@@ . @@ @@@@@@@@@@@ @@ \\ ", + " /@@@ . @@@@@@ o @ @@@@@@@@@@@@@ o @@@@ \\ ", + " /@@@@@ @ . @@@@@@@@@@@@@@ @@@@@ \\ ", + " |@@@@@ O `.-./ . . @@@@@@@@@@@@@ @@@ | ", + " / @@@@@ --`-' o @@@@@@@@@@@ @@@ . \\", + " |@ @@@@ . @ @ ` @ @@ . @@@@@@ |", + " | @@ o @@ . @@@@@@ |", + " | . @ @ @ o @@ o @@@@@@. |", + " \\ @ @ @ .-. @@@@ @@@ /", + " | @ @ @ `-' . @@@@ . . | ", + " \\ . o @ @@@@ . @@ . . / ", + " \\ @@@ @@@@@@ . o / ", + " \\ @@@@@ @@\\@@ / O . / ", + " \\ o @@@ \\ \\ / __ . . .--. / ", + " \\ . . \\.-.--- `--' / ", + " `. `-' . .' ", + " `. o / | ` O . .' ", + " `-. / | o .-' ", + " `-. . . .-' ", + " `---. . .---' ", + " `--------------' "}; + static char hubert29[29][59] = { + " .--------------. ", + " .---' o . `---. ", + " .-' . O . . `-. ", + " .-' @@@@@@ . `-. ", + " .'@@ @@@@@@@@@@@ @@@@@@@ . `. ", + " .'@@@ @@@@@ ___====-_ _-====___ @ `. ", + " /@@@ o _--~~~#####// \\\\#####~~~--_ O \\ ", + " / _-~##########// ( ) \\\\##########~-_ . \\ ", + " /@ o -############// :\\^^/: \\\\############- @@ \\ ", + " /@@@ _~############// (@::@) \\\\############~_ @@ \\ ", + " /@@@ ~#############(( \\\\// ))#############~ @@ \\ ", + " |@@ -###############\\\\ (oo) //###############- @ | ", + " / @ -#################\\\\ / \"\" \\ //#################- . \\", + " |@ -###################\\\\/ \\//###################- |", + " | _#/:##########/\\######( /\\ )######/\\##########:\\#_ |", + " | :/ :#/\\#/\\#/\\/ \\#/\\##\\ : : /##/\\#/ \\/\\#/\\#/\\#: \\: |", + " \\ \" :/ V V \" V \\#\\: : : :/#/ V \" V V \\: \" /", + " | @ \" \" \" \" / : : : : \\ \" \" \" \" | ", + " \\ . o @ @@@@ ( : : : : ) @@ . . / ", + " \\ @@@ @@@@ __\\ : : : : /__ o / ", + " \\ @@@@@ @@\\@(vvv(VVV)(VVV)vvv) . / ", + " \\ o @@@ \\ \\ / __ . . .--. / ", + " \\ . . \\.-.--- `--' / ", + " `. `-' . .' ", + " `. o / | ` O . .' ", + " `-. / | o .-' ", + " `-. . . .-' ", + " `---. . .---' ", + " `--------------' "}; + static char background32[32][65] = { + " .--------------. ", + " .----' o . `----. ", + " .-' . O . . `-. ", + " .-' @@@@@@ . `-. ", + " .'@ @@@@@@@@@@@ @@@@@@@@ . `. ", + " .'@@ @@@@@@@@@@@@@@ @@@@@@@@@@ `. ", + " .'@@@ o @@@@@@@@@@@@@@ @@@@@@@@@@ o `. ", + " /@@@ @@@@@@@@@@@@@@ @ @@@@@@@@@@ @@ . \\ ", + " / @@@@@@@@@@@ . @@ @@@@@@@@@@@@ @@ \\ ", + " /@ o . @@@@@@ o @ @@@@@@@@@@@@@@ o @@@@ \\ ", + " /@@@ . @@@@@@@@@@@@@@@ @@@@@ \\ ", + " /@@@@@ @ . @@@@@@@@@@@@@@ @@@ \\ ", + " |@@@@@ o `.-./ . @@@@@@@@@@@@ @@@ . | ", + " / @@@@@ __`-' o @@ . @@@@@@ \\", + " |@ @@@@ . @ ` @ @@ . @@@@@@ |", + " | @@ @ o @@@ o @@@@@@. |", + " | @ @@@@@ @@@ |", + " | . . @ @ @ o @@@@@ . . |", + " \\ @ .-. . @@@ . . /", + " | @ @ @ @ `-' . / ", + " \\ . @ @ . o / ", + " \\ o @@@@ . . / ", + " \\ @@@ @@@@@@ . o / ", + " \\ @@@@@ @@\\@@ / o . / ", + " \\ o @@@ \\ \\ / ___ . . .--. / ", + " `. . \\.-.--- `--' .' ", + " `. `-' . .' ", + " `. o / | O . .' ", + " `-. / | o .-' ", + " `-. . . .-' ", + " `----. . .----' ", + " `--------------' "}; + + static char qlits[4][16] = { + "New Moon + ", + "First Quarter +", + "Full Moon + ", + "Last Quarter + ", + }; + static char nqlits[4][16] = { + "New Moon - ", + "First Quarter -", + "Full Moon - ", + "Last Quarter - ", + }; + + struct tm* tmP; + double jd, pctphase, angphase, cphase, aom, cdist, cangdia, csund, csuang; + double phases[2], which[2]; + long clocknow; + int atflrlen, atflridx, numcols, lin, col, midlin; + double mcap, yrad, xrad, y, xright, xleft; + int colright, colleft; + char c; + + /* Find the length of the atfiller string. */ + atflrlen = strlen( atfiller ); + + /* Figure out the phase. */ + jd = unix_to_julian( t ); + pctphase = phase( jd, &cphase, &aom, &cdist, &cangdia, &csund, &csuang ); + angphase = pctphase * 2.0 * PI; + mcap = -cos( angphase ); + + /* Get now for use as a random number. */ + (void) time( &clocknow ); + + /* Randomly cheat and generate Hubert. */ + if ( clocknow % 13 == 3 && cphase > 0.8 ) + { + numlines = 29; + clocknow = 3; + } + + /* Figure out how big the moon is. */ + yrad = numlines / 2.0; + xrad = yrad / ASPECTRATIO; + numcols = xrad * 2; + + /* Figure out some other random stuff. */ + midlin = numlines / 2; + phasehunt2( jd, phases, which ); + + /* Now output the moon, a slice at a time. */ + atflridx = 0; + for ( lin = 0; lin < numlines; lin = lin + 1 ) + { + /* Compute the edges of this slice. */ + y = lin + 0.5 - yrad; + xright = xrad * sqrt( 1.0 - ( y * y ) / ( yrad * yrad ) ); + xleft = -xright; + if ( angphase >= 0.0 && angphase < PI ) + xleft = mcap * xleft; + else + xright = mcap * xright; + colleft = (int) (xrad + 0.5) + (int) (xleft + 0.5); + colright = (int) (xrad + 0.5) + (int) (xright + 0.5); + + /* Now output the slice. */ + for ( col = 0; col < colleft; ++col ) + putchar( ' ' ); + for ( ; col <= colright; ++col ) + { + switch ( numlines ) + { + case 18: + c = background18[lin][col]; + break; + case 19: + tmP = localtime( &t ); + if ( tmP->tm_mon == 9 && + clocknow % ( 33 - tmP->tm_mday ) == 1 ) + c = pumpkin19[lin][col]; + else + c = background19[lin][col]; + break; + case 21: + c = background21[lin][col]; + break; + case 22: + c = background22[lin][col]; + break; + case 23: + c = background23[lin][col]; + break; + case 24: + c = background24[lin][col]; + break; + case 29: + if ( clocknow % 23 == 3 ) + c = hubert29[lin][col]; + else + c = background29[lin][col]; + break; + case 32: + c = background32[lin][col]; + break; + default: + c = '@'; + } + if ( c != '@' ) + putchar( c ); + else + { + putchar( atfiller[atflridx] ); + atflridx = ( atflridx + 1 ) % atflrlen; + } + } +#ifdef notdef + for ( ; col <= numcols; ++col ) + putchar( ' ' ); +#endif /* notdef */ + + if ( numlines <= 27 ) + { + /* Output the end-of-line information, if any. */ + if ( lin == midlin - 2 ) + { + fputs( "\t ", stdout ); + fputs( qlits[(int) (which[0] * 4.0 + 0.001)], stdout ); + } + else if ( lin == midlin - 1) + { + fputs( "\t ", stdout ); + putseconds( (int) ( ( jd - phases[0] ) * SECSPERDAY ) ); + } + else if ( lin == midlin ) + { + fputs( "\t ", stdout ); + fputs( nqlits[(int) (which[1] * 4.0 + 0.001)], stdout ); + } + else if ( lin == midlin + 1 ) + { + fputs( "\t ", stdout ); + putseconds( (int) ( ( phases[1] - jd ) * SECSPERDAY ) ); + } + } + + putchar( '\n' ); + } + + } + + +/* Main program. */ + +int +main( int argc, char** argv ) + { + time_t t; + char buf[100]; + int numlines, argn; + char* usage = "usage: %s [-l ] []\n"; + + /* Parge args. */ + argn = 1; + /* Check for -l flag. */ + numlines = DEFAULTNUMLINES; + if ( argc - argn >= 1 ) + { + if ( argv[argn][0] == '-' ) + { + if ( argv[argn][1] != 'l' || argv[argn][2] != '\0' ) + { + fprintf( stderr, usage, argv[0] ); + exit( 1 ); + } + else + { + if ( argc - argn < 2 ) + { + fprintf( stderr, usage, argv[0] ); + exit( 1 ); + } + if ( sscanf( argv[argn + 1], "%d", &numlines ) != 1 ) + { + fprintf( stderr, usage, argv[0] ); + exit( 1 ); + } + argn += 2; + } + } + } + + /* Figure out what date and time to use. */ + if ( argc - argn == 0 ) + { + /* No arguments present - use the current date and time. */ + t = time( (time_t) 0 ); + } + else if ( argc - argn == 1 || argc - argn == 2 || argc - argn == 3 ) + { + /* One, two, or three args - use them. */ + strcpy( buf, argv[argn] ); + if ( argc - argn > 1 ) + { + strcat( buf, " " ); + strcat( buf, argv[argn + 1] ); + if ( argc - argn > 2 ) + { + strcat( buf, " " ); + strcat( buf, argv[argn + 2] ); + } + } + t = date_parse( buf ); + if ( t <= 0 ) + { + fprintf( stderr, "illegal date/time: %s\n", buf ); + exit( 1 ); + } + } + else + { + /* Too many args! */ + fprintf( stderr, usage, argv[0] ); + exit( 1 ); + } + + /* Pseudo-randomly decide what the moon is made of, and print it. */ + if ( t % 17 == 3 ) + putmoon( t, numlines, "GREENCHEESE" ); + else + putmoon( t, numlines, "@" ); + + /* All done. */ + exit( 0 ); + } diff --git a/src/Moon.zig b/src/Moon.zig new file mode 100644 index 0000000..4ad9b8c --- /dev/null +++ b/src/Moon.zig @@ -0,0 +1,71 @@ +const std = @import("std"); + +const c = @cImport({ + @cInclude("time.h"); + @cInclude("astro.h"); +}); + +pub const Phase = struct { + phase: f64, // 0.0 to 1.0 (0=new, 0.25=first quarter, 0.5=full, 0.75=last quarter) + illuminated: f64, // 0.0 to 1.0 + age_days: f64, // Age in days + distance_km: f64, + angular_diameter: f64, + + pub fn emoji(self: Phase) []const u8 { + const p = self.phase; + if (p < 0.0625) return "🌑"; // New moon + if (p < 0.1875) return "🌒"; // Waxing crescent + if (p < 0.3125) return "🌓"; // First quarter + if (p < 0.4375) return "🌔"; // Waxing gibbous + if (p < 0.5625) return "🌕"; // Full moon + if (p < 0.6875) return "🌖"; // Waning gibbous + if (p < 0.8125) return "🌗"; // Last quarter + if (p < 0.9375) return "🌘"; // Waning crescent + return "🌑"; // New moon + } + + pub fn day(self: Phase) u8 { + return @intFromFloat(@round(self.age_days)); + } +}; + +pub fn getPhase(timestamp: i64) Phase { + const julian = c.unix_to_julian(@intCast(timestamp)); + + var illuminated: f64 = 0; + var age_days: f64 = 0; + var distance: f64 = 0; + var angular_diameter: f64 = 0; + var sun_distance: f64 = 0; + var sun_angular_diameter: f64 = 0; + + const phase_angle = c.phase( + julian, + &illuminated, + &age_days, + &distance, + &angular_diameter, + &sun_distance, + &sun_angular_diameter, + ); + + return .{ + .phase = phase_angle, + .illuminated = illuminated, + .age_days = age_days, + .distance_km = distance, + .angular_diameter = angular_diameter, + }; +} + +test "moon phase calculation" { + // Test a known date: 2024-01-01 00:00:00 UTC + const timestamp: i64 = 1704067200; + const moon = getPhase(timestamp); + + try std.testing.expect(moon.phase >= 0.0 and moon.phase <= 1.0); + try std.testing.expect(moon.illuminated >= 0.0 and moon.illuminated <= 1.0); + try std.testing.expect(moon.age_days >= 0.0 and moon.age_days <= 29.53); + try std.testing.expect(moon.distance_km > 0); +} diff --git a/src/http/help.zig b/src/http/help.zig index c5b9b20..5c9cd0b 100644 --- a/src/http/help.zig +++ b/src/http/help.zig @@ -64,8 +64,8 @@ pub const help_page = \\ %f # temperature (feels like) \\ %w # wind \\ %l # location - \\ %m # * moon phase emoji - \\ %M # * moon day + \\ %m # moon phase emoji + \\ %M # moon day \\ %p # precipitation (mm) \\ %o # probability of precipitation \\ %P # pressure (hPa) diff --git a/src/render/Custom.zig b/src/render/Custom.zig index 8bb0ca6..165dc2c 100644 --- a/src/render/Custom.zig +++ b/src/render/Custom.zig @@ -2,6 +2,7 @@ const std = @import("std"); const types = @import("../weather/types.zig"); const emoji = @import("emoji.zig"); const utils = @import("utils.zig"); +const Moon = @import("../Moon.zig"); pub fn render(allocator: std.mem.Allocator, weather: types.WeatherData, format: []const u8, use_imperial: bool) ![]const u8 { var output: std.ArrayList(u8) = .empty; @@ -52,8 +53,16 @@ pub fn render(allocator: std.mem.Allocator, weather: types.WeatherData, format: const unit = if (use_imperial) "inHg" else "hPa"; try writer.print("{d:.2} {s}", .{ pressure, unit }); }, - 'm' => try writer.print("🌕", .{}), // Moon phase placeholder - 'M' => try writer.print("15", .{}), // Moon day placeholder + 'm' => { + const now = std.time.timestamp(); + const moon = Moon.getPhase(now); + try writer.print("{s}", .{moon.emoji()}); + }, + 'M' => { + const now = std.time.timestamp(); + const moon = Moon.getPhase(now); + try writer.print("{d}", .{moon.day()}); + }, 'o' => try writer.print("0%", .{}), // Probability of precipitation placeholder 'D' => try writer.print("06:00", .{}), // Dawn placeholder 'S' => try writer.print("07:30", .{}), // Sunrise placeholder