docs: update README, add wiki docs, and document types (P1)

This commit is contained in:
Aric Camarata 2026-05-30 20:16:21 -04:00
parent 882c586e2f
commit fc01b129f4
7 changed files with 234 additions and 47 deletions

34
.github/wiki/CODE_OF_CONDUCT.md vendored Normal file
View file

@ -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.

28
.github/wiki/SECURITY.md vendored Normal file
View file

@ -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.

3
.github/wiki/_Footer.md vendored Normal file
View file

@ -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)

20
.github/wiki/_Sidebar.md vendored Normal file
View file

@ -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)

View file

@ -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<PrayerTimesCard> createState() => _PrayerTimesCardState();
}
class _PrayerTimesCardState extends State<PrayerTimesCard> {
PrayerTimes? _times;
String? _error;
@override
void initState() {
super.initState();
_loadTimes();
}
Future<void> _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.

View file

@ -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).

View file

@ -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({