From fc01b129f4e4b47768b649ea482afc074a6cd37e Mon Sep 17 00:00:00 2001 From: Aric Camarata Date: Sat, 30 May 2026 20:16:21 -0400 Subject: [PATCH] docs: update README, add wiki docs, and document types (P1) --- .github/wiki/CODE_OF_CONDUCT.md | 34 ++++++ .github/wiki/SECURITY.md | 28 +++++ .github/wiki/_Footer.md | 3 + .github/wiki/_Sidebar.md | 20 +++ .github/wiki/examples/flutter-integration.md | 122 +++++++++++++++++++ README.md | 57 ++------- lib/src/types.dart | 17 +++ 7 files changed, 234 insertions(+), 47 deletions(-) create mode 100644 .github/wiki/CODE_OF_CONDUCT.md create mode 100644 .github/wiki/SECURITY.md create mode 100644 .github/wiki/_Footer.md create mode 100644 .github/wiki/_Sidebar.md create mode 100644 .github/wiki/examples/flutter-integration.md diff --git a/.github/wiki/CODE_OF_CONDUCT.md b/.github/wiki/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..1dd7df6 --- /dev/null +++ b/.github/wiki/CODE_OF_CONDUCT.md @@ -0,0 +1,34 @@ +# Code of Conduct + +## Our Pledge + +We as contributors and maintainers pledge to make participation in this project a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to a positive environment: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Accepting constructive criticism gracefully +- Focusing on what is best for the community +- Showing empathy toward other community members + +Examples of unacceptable behavior: + +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior. They will take appropriate and fair corrective action in response to any behavior they deem inappropriate, threatening, offensive, or harmful. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project maintainer at alisalaah@gmail.com. All complaints will be reviewed and investigated. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html. diff --git a/.github/wiki/SECURITY.md b/.github/wiki/SECURITY.md new file mode 100644 index 0000000..edf437c --- /dev/null +++ b/.github/wiki/SECURITY.md @@ -0,0 +1,28 @@ +# Security + +## Scope + +`pray_calc_dart` is a pure-math library with no network access, no file I/O, and no external dependencies beyond `nrel_spa`. The attack surface is limited to the mathematical functions themselves. + +The main concern is input validation: functions accept latitude (-90 to 90), longitude (-180 to 180), and UTC offset. Out-of-range values produce undefined behavior — clamp inputs to valid ranges before passing untrusted data. + +## Reporting a Vulnerability + +If you discover a security issue (for example, a case where malformed input causes unexpected behavior or crashes), please report it privately before filing a public issue. + +**Contact:** alisalaah@gmail.com + +Include: + +1. A description of the vulnerability +2. Steps to reproduce it +3. The version of `pray_calc_dart` where you observed the issue +4. Any suggested fix if you have one + +You can expect an acknowledgment within 48 hours and a resolution or status update within 7 days. + +## Known Limitations + +- Prayer times are computed using the NREL Solar Position Algorithm. Accuracy is approximately one second relative to the reference implementation. Results near polar latitudes (above 65 degrees N/S) should be treated as estimates. +- The MCW seasonal model uses empirical piecewise-linear functions. Accuracy at extreme latitudes degrades gracefully rather than producing errors, but times may differ from local observation. +- Time zone handling is the caller's responsibility. The library accepts a UTC offset in hours and returns fractional hours in that offset. DST adjustments must be applied by the caller. diff --git a/.github/wiki/_Footer.md b/.github/wiki/_Footer.md new file mode 100644 index 0000000..1c4babd --- /dev/null +++ b/.github/wiki/_Footer.md @@ -0,0 +1,3 @@ +[![pub package](https://img.shields.io/pub/v/pray_calc_dart.svg)](https://pub.dev/packages/pray_calc_dart)   [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/acamarata/pray-calc-dart/blob/main/LICENSE) + +[pub.dev](https://pub.dev/packages/pray_calc_dart) | [GitHub](https://github.com/acamarata/pray-calc-dart) | [Issues](https://github.com/acamarata/pray-calc-dart/issues) diff --git a/.github/wiki/_Sidebar.md b/.github/wiki/_Sidebar.md new file mode 100644 index 0000000..f4cc4ec --- /dev/null +++ b/.github/wiki/_Sidebar.md @@ -0,0 +1,20 @@ +## pray_calc_dart + +- [Home](Home) +- [API Reference](API-Reference) + +**Guides** +- [Quickstart](guides/quickstart) +- [Advanced Usage](guides/advanced) + +**Examples** +- [Basic Usage](examples/basic-usage) +- [Flutter Integration](examples/flutter-integration) + +**Project** +- [Code of Conduct](CODE_OF_CONDUCT) +- [Security](SECURITY) + +--- + +[pub.dev](https://pub.dev/packages/pray_calc_dart) | [GitHub](https://github.com/acamarata/pray-calc-dart) diff --git a/.github/wiki/examples/flutter-integration.md b/.github/wiki/examples/flutter-integration.md new file mode 100644 index 0000000..50a825b --- /dev/null +++ b/.github/wiki/examples/flutter-integration.md @@ -0,0 +1,122 @@ +# Flutter Integration + +This example shows how to use `pray_calc_dart` in a Flutter widget. It reads the device's current location using `geolocator` and displays the current prayer times for that location. + +## Dependencies + +Add to `pubspec.yaml`: + +```yaml +dependencies: + pray_calc_dart: ^1.0.0 + geolocator: ^14.0.0 +``` + +## PrayerTimesCard Widget + +```dart +import 'package:flutter/material.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:pray_calc_dart/pray_calc_dart.dart'; + +class PrayerTimesCard extends StatefulWidget { + const PrayerTimesCard({super.key}); + + @override + State createState() => _PrayerTimesCardState(); +} + +class _PrayerTimesCardState extends State { + PrayerTimes? _times; + String? _error; + + @override + void initState() { + super.initState(); + _loadTimes(); + } + + Future _loadTimes() async { + try { + final permission = await Geolocator.requestPermission(); + if (permission == LocationPermission.denied || + permission == LocationPermission.deniedForever) { + setState(() => _error = 'Location permission denied.'); + return; + } + + final pos = await Geolocator.getCurrentPosition(); + + // UTC offset in hours — derive from the device's local time. + final utcOffset = + DateTime.now().timeZoneOffset.inMinutes / 60.0; + + final times = getTimes( + DateTime.now(), + pos.latitude, + pos.longitude, + utcOffset, + ); + + setState(() => _times = times); + } catch (e) { + setState(() => _error = e.toString()); + } + } + + @override + Widget build(BuildContext context) { + if (_error != null) { + return Text('Error: $_error'); + } + if (_times == null) { + return const CircularProgressIndicator(); + } + + final rows = [ + ('Fajr', _times!.fajr), + ('Sunrise', _times!.sunrise), + ('Dhuhr', _times!.dhuhr), + ('Asr', _times!.asr), + ('Maghrib', _times!.maghrib), + ('Isha', _times!.isha), + ('Qiyam', _times!.qiyam), + ]; + + return Card( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: rows.map((r) { + final (name, hours) = r; + return Padding( + padding: const EdgeInsets.symmetric(vertical: 2), + child: Row( + children: [ + SizedBox( + width: 80, + child: Text( + name, + style: const TextStyle(fontWeight: FontWeight.w600), + ), + ), + Text(formatTime(hours)), + ], + ), + ); + }).toList(), + ), + ), + ); + } +} +``` + +## Notes + +- `getTimes` is a pure synchronous function. Only the location request is async. +- Pass `hanafi: true` to `getTimes` for the Hanafi Asr convention (shadow factor 2 instead of 1). +- `formatTime` returns `'N/A'` for unreachable events (polar nights). Handle this in the UI if targeting high-latitude users. +- For production use, cache the result and re-compute once per day or on location change. Prayer times change by only a few minutes per day at most latitudes. diff --git a/README.md b/README.md index 975143c..dfeb56a 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ [![pub package](https://img.shields.io/pub/v/pray_calc_dart.svg)](https://pub.dev/packages/pray_calc_dart) [![CI](https://github.com/acamarata/pray-calc-dart/actions/workflows/ci.yml/badge.svg)](https://github.com/acamarata/pray-calc-dart/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) +[![Wiki](https://img.shields.io/badge/docs-wiki-blue)](https://github.com/acamarata/pray-calc-dart/wiki) Islamic prayer times for Dart and Flutter. Pure Dart port of [pray-calc](https://github.com/acamarata/pray-calc), implementing the MCW seasonal model and dynamic twilight angles. Uses [nrel_spa](https://github.com/acamarata/nrel-spa-dart) for the NREL Solar Position Algorithm. @@ -34,61 +35,23 @@ void main() { ## API -### `getTimes(date, lat, lng, tz, {elevation, temperature, pressure, hanafi})` +Full API documentation, guides, and examples are in the [wiki](https://github.com/acamarata/pray-calc-dart/wiki). -Computes all prayer times for a given date and location. +### Core functions -| Parameter | Type | Default | Description | -| --- | --- | --- | --- | -| `date` | `DateTime` | required | Local date (time-of-day ignored) | -| `lat` | `double` | required | Latitude (-90 to 90, south negative) | -| `lng` | `double` | required | Longitude (-180 to 180, west negative) | -| `tz` | `double` | required | UTC offset in hours (e.g., -5 for EST) | -| `elevation` | `double` | 0 | Observer elevation in meters | -| `temperature` | `double` | 15 | Ambient temperature in Celsius | -| `pressure` | `double` | 1013.25 | Atmospheric pressure in mbar | -| `hanafi` | `bool` | false | Hanafi Asr (2x shadow) vs Shafi'i (1x) | - -Returns a `PrayerTimes` object with fractional-hour values for: `qiyam`, `fajr`, `sunrise`, `noon`, `dhuhr`, `asr`, `maghrib`, `isha`, and the computed `angles`. - -### `getAngles(date, lat, lng, {elevation, temperature, pressure})` - -Computes dynamic twilight depression angles using the three-layer model: - -1. MCW seasonal base (piecewise-linear, latitude-dependent) -2. Ephemeris corrections (Earth-Sun distance, Fourier season smoothing) -3. Environmental corrections (elevation dip, atmospheric refraction) - -Returns `TwilightAngles` with `fajrAngle` and `ishaAngle` in degrees, clipped to [10, 22]. - -### `getSpa(date, lat, lng, tz, {...})` - -Full NREL Solar Position Algorithm. Accurate to +/-0.0003 degrees for zenith angle. Supports custom zenith angles for twilight calculations. - -### `formatTime(hours)` - -Converts fractional hours to an `HH:MM:SS` string. Returns `"N/A"` for non-finite values. - -### Additional Functions - -- `solarEphemeris(jd)` -- Jean Meeus Ch. 25 low-precision ephemeris -- `toJulianDate(date)` -- DateTime to Julian Date -- `getAsr(solarNoon, latitude, declination, {hanafi})` -- Asr computation -- `getQiyam(fajrTime, ishaTime)` -- Last third of the night -- `getMscFajr(date, latitude)` -- MCW Fajr offset in minutes -- `getMscIsha(date, latitude, [shafaq])` -- MCW Isha offset in minutes -- `minutesToDepression(minutes, latDeg, declDeg)` -- Time to angle conversion +| Function | Description | +| --- | --- | +| `getTimes(date, lat, lng, tz, {...})` | All prayer times for a date and location | +| `getAngles(date, lat, lng, {...})` | Dynamic Fajr/Isha depression angles | +| `getSpa(date, lat, lng, tz, {...})` | NREL Solar Position Algorithm (re-export) | +| `formatTime(hours)` | Fractional hours to `HH:MM:SS` string | ## Dynamic Angle Algorithm -Fixed-angle methods (ISNA 15 degrees, MWL 18 degrees, etc.) produce inaccurate Fajr times at latitudes above 45 degrees N/S. The dynamic method adapts the depression angle based on season, latitude, Earth-Sun distance, and local atmospheric conditions. +Fixed-angle methods (ISNA 15 degrees, MWL 18 degrees) produce inaccurate Fajr times at latitudes above 45 degrees N/S. The dynamic method adapts the depression angle based on season, latitude, Earth-Sun distance, and local atmospheric conditions. Result: approximately 18 degrees at the equator, approximately 12-14 degrees at 50-55 degrees N in summer. Matches observational data from the Moonsighting Committee Worldwide. -## Architecture - -Built in three layers: (1) [nrel_spa](https://pub.dev/packages/nrel_spa) provides the solar ephemeris foundation; (2) the MSC piecewise model computes seasonal minute offsets which are converted to depression angles via spherical trigonometry; (3) physics corrections (Earth-Sun distance, refraction, elevation dip) adjust the angle within [10°, 22°] bounds. - ## Compatibility Dart SDK 3.7.0+. Works in Flutter (iOS, Android, Web, Desktop), Dart CLI, and server-side Dart. Single dependency: [nrel_spa](https://pub.dev/packages/nrel_spa). diff --git a/lib/src/types.dart b/lib/src/types.dart index 9c35d53..a27a92c 100644 --- a/lib/src/types.dart +++ b/lib/src/types.dart @@ -62,14 +62,31 @@ class PrayerTimes { /// Prayer times formatted as HH:MM:SS strings. class FormattedPrayerTimes { + /// Start of the last third of the night (Qiyam al-Layl), as HH:MM:SS. final String qiyam; + + /// True dawn (Subh Sadiq), as HH:MM:SS. final String fajr; + + /// Astronomical sunrise, as HH:MM:SS. final String sunrise; + + /// Solar noon (exact geometric transit), as HH:MM:SS. final String noon; + + /// Dhuhr (2.5 minutes after solar noon), as HH:MM:SS. final String dhuhr; + + /// Asr (Shafi'i or Hanafi shadow convention), as HH:MM:SS. final String asr; + + /// Maghrib (sunset), as HH:MM:SS. final String maghrib; + + /// Isha (nightfall, end of shafaq), as HH:MM:SS. final String isha; + + /// Dynamic twilight angles used for this calculation. final TwilightAngles angles; const FormattedPrayerTimes({