Portal, ND Hourly Weather Updates

Feedback E-mail the Weather

Quick Clicks

Weather World

Portal, ND Weather Maps

°F °C°F °C
Portal Weather Map
UTC Time: 9/12/2026, 13:38
Your Time:

Aeronautical Observation: Automated synoptic observations via NWS Gateway.
Auto-refresh in: 5:02

Portal, ND Regional Weather

Expires:202609121400;;326615
ASUS43 KBIS 121331
RWRND 

NORTH DAKOTA REGIONAL WEATHER ROUNDUP
NATIONAL WEATHER SERVICE BISMARCK ND
800 AM CDT SAT SEP 12 2026

NOTE: "FAIR" INDICATES FEW OR NO CLOUDS BELOW 12,000 FEET WITH NO
SIGNIFICANT WEATHER AND/OR OBSTRUCTIONS TO VISIBILITY.



NDZ001>003-009>011-017-121400-
NORTHWEST NORTH DAKOTA
  
CITY           SKY/WX    TMP DP  RH WIND       PRES   REMARKS
WILLISTON      MOCLDY    41  40  96 W10       30.10R                  
MINOT          PTCLDY    46  42  86 W14       30.04R                  
MINOT AFB      CLEAR     46  42  86 NW12      30.00S                  
$$
 

SKY/WX - Weather  TMP - Temperature  DP - Dew Point  RH - Humidity
WIND - Wind Direction/Speed PRES - Barometric Pressure VSB - Visibility
WCI - Wind Chill Index E13G22 Wind - East Wind 13 mph/Gusts of 22 mph
2026-09-12 09:38:54 %%CODE $stateCode = 'ne'; $cwaKey = 'oax'; $currentCity = 'omaha'; $refLat = 41.26390; $refLon = -96.01170; $refZone = 'NEZ052'; $targetLimit = 30; $enableMissingLinkLogging = true; $stateCode = !empty($GET['state']) ? $GET['state'] : (!empty($stateCode) ? $stateCode : 'ct'); $cwaKey = !empty($GET['cwa']) ? $GET['cwa'] : (!empty($cwaKey) ? $cwaKey : 'box'); $stateCode = strtolower(preg_replace('/[^a-z]/i', '', $stateCode)); $cwaKey = strtolower(preg_replace('/[^a-z0-9]/i', '', $cwaKey)); if (!function_exists('getWxDbConnection')) { function getWxDbConnection() { static $pdo = null; if ($pdo === null) { $dbConfigFile = '/home/wxworld/public_html/database.ini.php'; if (file_exists($dbConfigFile)) { require $dbConfigFile; try { $dsn = "mysql:host={$dbserver};dbname={$dbdatabase};charset=utf8mb4"; $pdo = new PDO($dsn, $dbuser, $dbpassword, array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => true, )); } catch (PDOException $e) { error_log("WeatherWorld DB Connection Error: " . $e->getMessage()); return null; } } } return $pdo; } } if (!function_exists('getNwsConditionMap')) { function getNwsConditionMap() { return array( 'SUNNY' => array('label' => 'Sunny', 'icon' => 'wi-day-sunny'), 'CLEAR' => array('label' => 'Clear', 'icon' => 'wi-day-sunny'), 'PTCLDY' => array('label' => 'Partly Cloudy', 'icon' => 'wi-day-cloudy'), 'MOCLDY' => array('label' => 'Mostly Cloudy', 'icon' => 'wi-day-cloudy-high'), 'CLOUDY' => array('label' => 'Cloudy', 'icon' => 'wi-cloudy'), 'HAZE' => array('label' => 'Haze', 'icon' => 'wi-day-haze'), 'WINDY' => array('label' => 'Windy', 'icon' => 'wi-day-windy'), 'DRZL' => array('label' => 'Drizzle', 'icon' => 'wi-day-sprinkle'), 'DRIZZLE' => array('label' => 'Drizzle', 'icon' => 'wi-day-sprinkle'), 'SPRINKLES' => array('label' => 'Sprinkles', 'icon' => 'wi-day-sprinkle'), 'RAIN' => array('label' => 'Rain', 'icon' => 'wi-day-rain'), 'SHWRS' => array('label' => 'Rain Showers', 'icon' => 'wi-day-showers'), 'RNSNOW' => array('label' => 'Rain & Snow Mix', 'icon' => 'wi-day-rain-mix'), 'SNOW' => array('label' => 'Snow', 'icon' => 'wi-day-snow'), 'SNOSHWR' => array('label' => 'Snow Showers', 'icon' => 'wi-day-snow'), 'SNOWSHWRS' => array('label' => 'Snow Showers', 'icon' => 'wi-day-snow'), 'SLEET' => array('label' => 'Sleet', 'icon' => 'wi-day-sleet'), 'TSTRMS' => array('label' => 'Thunderstorms', 'icon' => 'wi-day-thunderstorm'), 'TSTMS' => array('label' => 'Thunderstorms', 'icon' => 'wi-day-thunderstorm'), 'VRYHOT' => array('label' => 'Very Hot', 'icon' => 'wi-hot') ); } } if (!function_exists('renderSftAreaForecast')) { function renderSftAreaForecast($parsedDataCollection, $defaultStateCode = 'ct') { global $refLat, $refLon, $refZone, $targetLimit, $currentCity, $stateCode, $enableMissingLinkLogging; $map = getNwsConditionMap(); $pdo = getWxDbConnection(); $maxLimit = isset($targetLimit) ? intval($targetLimit) : 30; $lLat = !empty($refLat) ? floatval($refLat) : 45.18400; $lLon = !empty($refLon) ? floatval($refLon) : -84.95670; $zoneAliasMap = array( 'COZ040' => 'COZ038', ); $resolvedRefZone = !empty($refZone) ? strtoupper(trim($refZone)) : ''; if (isset($zoneAliasMap[$resolvedRefZone])) { $resolvedRefZone = $zoneAliasMap[$resolvedRefZone]; } $forcedState = !empty($stateCode) ? strtolower($stateCode) : strtolower($defaultStateCode); $forcedCityName = !empty($currentCity) ? trim($currentCity) : ''; $masterZones = array(); foreach ($parsedDataCollection as $parsedData) { if (isset($parsedData['zones']) && is_array($parsedData['zones'])) { foreach ($parsedData['zones'] as $zKey => $zVal) { $zKeyUpper = strtoupper(trim($zKey)); if (!isset($masterZones[$zKeyUpper])) { $masterZones[$zKeyUpper] = $zVal; } } } } if (empty($masterZones)) { return 'No regional zone data found in cache payloads.'; } $dbZoneCoords = array(); $targetZones = array_keys($masterZones); if ($pdo && !empty($targetZones)) { $chunkedZones = array_chunk($targetZones, 50); foreach ($chunkedZones as $chunk) { $placeholders = array(); $params = array(); foreach ($chunk as $i => $zKey) { $ph = ":z_" . md5($zKey) . "{$i}"; $placeholders[] = $ph; $params[$ph] = strtoupper(trim($zKey)); } $sql = "SELECT UPPER(a.wxzone) AS wxzone_raw, p.lat, p.lon, p.name, p.state, " . "(3959 * ACOS(COS(RADIANS(:currentLat)) * COS(RADIANS(p.lat)) * COS(RADIANS(p.lon) - RADIANS(:currentLon)) + SIN(RADIANS(:currentLat)) * SIN(RADIANS(p.lat)))) AS distance " . "FROM places p " . "JOIN alt_zone_info a ON p.places_id = a.places_id " . "WHERE UPPER(TRIM(a.wxzone)) IN (" . implode(',', $placeholders) . ") " . "ORDER BY distance ASC"; $stmt = $pdo->prepare($sql); $execParams = array_merge(array(':currentLat' => $lLat, ':currentLon' => $lLon), $params); $stmt->execute($execParams); while ($row = $stmt->fetch()) { $zUpper = strtoupper($row['wxzone_raw']); if (!isset($dbZoneCoords[$zUpper])) { $dbZoneCoords[$zUpper] = array( 'lat' => floatval($row['lat']), 'lon' => floatval($row['lon']), 'nearest_place' => $row['name'] . ', ' . $row['state'] ); } } } } $tempRankedZones = array(); foreach ($masterZones as $zoneKey => $zoneData) { $zKeyUpper = strtoupper(trim($zoneKey)); $zStatePrefix = strtolower(substr($zKeyUpper, 0, 2)); $isRef = ($zKeyUpper === $resolvedRefZone); $zoneState = !empty($zoneData['state_code']) ? strtolower($zoneData['state_code']) : $zStatePrefix; if (!preg_match('/^[a-z]{2}$/', $zoneState)) { $zoneState = $forcedState; } $closestCity = ''; if (!empty($zoneData['cities'][0])) { $closestCity = trim($zoneData['cities'][0]) . ', ' . strtoupper($zoneState); } elseif (!empty($zoneData['primary_name'])) { $closestCity = trim($zoneData['primary_name']) . ', ' . strtoupper($zoneState); } elseif (!empty($zoneData['zone_name'])) { $closestCity = trim($zoneData['zone_name']) . ', ' . strtoupper($zoneState); } elseif (!empty($zoneData['name'])) { $closestCity = trim($zoneData['name']) . ', ' . strtoupper($zoneState); } else { $closestCity = trim($zKeyUpper) . ', ' . strtoupper($zoneState); } if ($isRef) { if ($forcedCityName !== '') { $formattedRefCity = ucwords(strtolower($forcedCityName)); $closestCity = $formattedRefCity . ', ' . strtoupper($forcedState); } $zoneState = $forcedState; } $distance = 9999.0; $zoneLat = null; $zoneLon = null; if (isset($dbZoneCoords[$zKeyUpper])) { $zoneLat = $dbZoneCoords[$zKeyUpper]['lat']; $zoneLon = $dbZoneCoords[$zKeyUpper]['lon']; } elseif (isset($zoneData['lat']) && isset($zoneData['lon'])) { $zoneLat = floatval($zoneData['lat']); $zoneLon = floatval($zoneData['lon']); } if ($zoneLat !== null && $zoneLon !== null) { $theta = $lLon - $zoneLon; $dist = sin(deg2rad($lLat)) * sin(deg2rad($zoneLat)) + cos(deg2rad($lLat)) * cos(deg2rad($zoneLat)) * cos(deg2rad($theta)); $dist = acos(min(1.0, max(-1.0, $dist))); $distance = rad2deg($dist) * 69.09; } if ($isRef) { $distance = -1.0; } $parts = explode(',', $closestCity); $cleanCityName = trim($parts[0]); $displayState = isset($parts[1]) ? trim($parts[1]) : strtoupper($zoneState); $citySignature = strtolower($cleanCityName) . '|' . strtolower($displayState); if (!isset($tempRankedZones[$citySignature])) { $tempRankedZones[$citySignature] = array( 'zoneKey' => $zoneKey, 'zoneData' => $zoneData, 'zoneState' => $zoneState, 'cleanCityName' => $cleanCityName, 'displayState' => $displayState, 'distance' => $distance, 'is_ref' => $isRef ); } else { if ($isRef || (!$tempRankedZones[$citySignature]['is_ref'] && $distance < $tempRankedZones[$citySignature]['distance'])) { $tempRankedZones[$citySignature] = array( 'zoneKey' => $zoneKey, 'zoneData' => $zoneData, 'zoneState' => $zoneState, 'cleanCityName' => $cleanCityName, 'displayState' => $displayState, 'distance' => $distance, 'is_ref' => $isRef ); } } } $rankedZones = array_values($tempRankedZones); usort($rankedZones, 'sft_sort_ranked_zones'); $existingPlacesMap = array(); if ($pdo && !empty($rankedZones)) { $uniquePairs = array(); foreach ($rankedZones as $item) { $pairKey = strtolower($item['cleanCityName']) . '|' . strtolower($item['displayState']); if ($item['is_ref']) { $existingPlacesMap[$pairKey] = true; } else { $uniquePairs[$pairKey] = array( 'name' => $item['cleanCityName'], 'state' => $item['displayState'] ); } } if (!empty($uniquePairs)) { $pairChunks = array_chunk(array_values($uniquePairs), 50); foreach ($pairChunks as $chunk) { $tuples = array(); $params = array(); $idx = 0; foreach ($chunk as $p) { $namePlaceholder = ":pname{$idx}"; $statePlaceholder = ":pstate_{$idx}"; $tuples[] = "({$namePlaceholder}, {$statePlaceholder})"; $params[$namePlaceholder] = $p['name']; $params[$statePlaceholder] = $p['state']; $idx++; } $sql = "SELECT DISTINCT name, state FROM places WHERE (name, state) IN (" . implode(',', $tuples) . ")"; $stmt = $pdo->prepare($sql); $stmt->execute($params); while ($row = $stmt->fetch()) { $mapKey = strtolower(trim($row['name'])) . '|' . strtolower(trim($row['state'])); $existingPlacesMap[$mapKey] = true; } } } } $cityCounter = 0; $html = ''; foreach ($rankedZones as $item) { if ($cityCounter >= $maxLimit) { break; } $zoneKey = $item['zoneKey']; $zoneData = $item['zoneData']; $cleanCityName = $item['cleanCityName']; $displayState = $item['displayState']; $zoneState = $item['zoneState']; $cityCounter++; $displayTitle = $cleanCityName . ', ' . strtoupper($displayState); $citySlug = strtolower(str_replace(' ', '+', $cleanCityName)); $forecastUrl = 'https://www.weatherworld.com/14dayweather/' . strtolower($displayState) . '/' . $citySlug . '.html'; $warningUrl = 'https://www.weatherworld.com/stormwarnings/' . strtolower($displayState) . '/' . $citySlug . '.html'; $pairKey = strtolower($cleanCityName) . '|' . strtolower($displayState); $placeExistsInDb = isset($existingPlacesMap[$pairKey]) || $item['is_ref']; if ($placeExistsInDb) { $titleContent = '' . htmlspecialchars($displayTitle) . ''; } else { $titleContent = htmlspecialchars($displayTitle); if (!empty($enableMissingLinkLogging)) { $logMessage = sprintf("[%s] Unlinked place: %s (Zone: %s) not found in database.\n", date('Y-m-d H:i:s'), $displayTitle, $zoneKey); @error_log($logMessage, 3, '/home/wxworld/public_html/php-includes/missing_links.log'); } } $forecastPeriods = array(); if (isset($zoneData['forecast_periods']) && is_array($zoneData['forecast_periods'])) { $forecastPeriods = $zoneData['forecast_periods']; } elseif (isset($zoneData['periods']) && is_array($zoneData['periods'])) { $forecastPeriods = $zoneData['periods']; } elseif (isset($zoneData['properties']['periods']) && is_array($zoneData['properties']['periods'])) { $forecastPeriods = $zoneData['properties']['periods']; } $activeAlerts = array(); if (!empty($zoneData['active_alert'])) { if (is_array($zoneData['active_alert'])) { $activeAlerts = $zoneData['active_alert']; } else { $splitAlerts = preg_split('/(?=...[A-Z])/', $zoneData['active_alert']); foreach ($splitAlerts as $sa) { $trimmed = trim($sa); if ($trimmed !== '') $activeAlerts[] = $trimmed; } } } $columns = array(); foreach ($forecastPeriods as $period) { $pName = trim(isset($period['period']) ? $period['period'] : (isset($period['name']) ? $period['name'] : '')); $rawDate = isset($period['day_date']) ? $period['day_date'] : (isset($period['startTime']) ? substr($period['startTime'], 0, 10) : ''); $pDate = $rawDate !== '' ? date('m-d', strtotime($rawDate)) : ''; $temp = isset($period['hi_lo_temp']) ? $period['hi_lo_temp'] : (isset($period['temperature']) ? $period['temperature'] : ''); $cond = isset($period['condition']) ? $period['condition'] : (isset($period['shortForecast']) ? $period['shortForecast'] : ''); $icon = isset($period['condition_icon']) ? $period['condition_icon'] : (isset($period['icon']) ? $period['icon'] : ''); $popRaw = isset($period['pop']) ? $period['pop'] : (isset($period['probabilityOfPrecipitation']['value']) ? $period['probabilityOfPrecipitation']['value'] : ''); $pop = ($popRaw !== '' && $popRaw !== null) ? intval($popRaw) : 0; $desc = isset($period['day_night_zone_text']) ? $period['day_night_zone_text'] : (isset($period['detailed_forecast']) ? $period['detailed_forecast'] : (isset($period['detailedForecast']) ? $period['detailedForecast'] : '')); $isDaytimeFlag = isset($period['isDaytime']) ? $period['isDaytime'] : null; $pNameLower = strtolower($pName); if (strpos($pNameLower, 'early this morning') !== false || strpos($pNameLower, 'this morning') !== false) { $pName = 'This Morning'; } elseif (strpos($pNameLower, 'today') !== false && strpos($pNameLower, 'tonight') === false) { $pName = 'Today'; } $isNight = ($isDaytimeFlag !== null) ? !$isDaytimeFlag : (stripos($pName, 'night') !== false); if ($isNight) { $matched = false; foreach ($columns as &$col) { if (isset($col['full_date']) && $col['full_date'] === $rawDate && $col['low'] === '') { $col['low'] = $temp; $col['night_pop'] = $pop; if (!empty($desc)) $col['night_desc'] = $desc; $matched = true; break; } } unset($col); if ($matched) continue; } $columns[] = array( 'day' => $pName, 'date' => $pDate, 'full_date' => $rawDate, 'high' => !$isNight ? $temp : '', 'low' => $isNight ? $temp : '', 'condition' => $cond, 'condition_icon' => $icon, 'day_pop' => !$isNight ? $pop : 0, 'night_pop' => $isNight ? $pop : 0, 'day_desc' => !$isNight ? ($desc !== '' ? $desc : ($pName . ': ' . $cond)) : '', 'night_desc' => $isNight ? ($desc !== '' ? $desc : ($pName . ': ' . $cond)) : '' ); } $html .= ''; $html .= ' ' . $titleContent . ''; if (!empty($activeAlerts)) { foreach ($activeAlerts as $alertText) { $cleanAlert = trim($alertText, '. '); if ($cleanAlert !== '') { $html .= ''; $html .= ' ' . htmlspecialchars(strtoupper($cleanAlert)) . ''; $html .= ''; } } } $html .= ''; $colIndex = 0; foreach ($columns as $col) { $rawWx = strtoupper($col['condition_icon'] !== '' ? $col['condition_icon'] : $col['condition']); $cond = isset($map[$rawWx]) ? $map[$rawWx] : array('label' => ($col['condition'] !== '' ? $col['condition'] : $rawWx), 'icon' => 'wi-cloud'); $displayPop = max($col['day_pop'], $col['night_pop']); $activeClass = ($colIndex === 0) ? ' active' : ''; $targetId = 'desc-' . md5($zoneKey . $cleanCityName) . '-' . $colIndex; $rawDayName = trim($col['day']); $cleanDayName = preg_replace('/]fa-moon[^>]>.?/i', 'Night', $rawDayName); if (stripos($cleanDayName, 'night') !== false || preg_match('/(/|to|thru|through|and|-)/i', $cleanDayName)) { $daysRegex = '/\b(Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday)\b/i'; $cleanDayName = preg_replace_callback($daysRegex, 'sft_ucfirst_substr', $cleanDayName); $cleanDayName = preg_replace('/\s[/-]\s*/', '-', $cleanDayName); } $html .= ''; $html .= '' . str_replace('','', $cleanDayName) . ''; $html .= ''; $html .= '' . htmlspecialchars($cond['label']) . ''; $html .= ''; if ($col['high'] !== '' && $col['low'] !== '') { $fHigh = (int)$col['high']; $fLow = (int)$col['low']; $cHigh = round(($fHigh - 32) * 5 / 9); $cLow = round(($fLow - 32) * 5 / 9); $html .= '' . $fHigh . '°/ ' . $fLow . '° '; $html .= '' . $cHigh . '°/ ' . $cLow . '°'; } elseif ($col['high'] !== '') { $fHigh = (int)$col['high']; $cHigh = round(($fHigh - 32) * 5 / 9); $html .= '' . $fHigh . '°' . $cHigh . '°'; } elseif ($col['low'] !== '') { $fLow = (int)$col['low']; $cLow = round(($fLow - 32) * 5 / 9); $html .= '' . $fLow . '°' . $cLow . '°'; } $html .= ''; $isPrecip = (stripos($col['condition'], 'rain') !== false || stripos($col['condition'], 'showers') !== false || stripos($col['condition'], 'snow') !== false || stripos($col['condition'], 'thunderstorm') !== false); if ($displayPop > 0) { $html .= ' ' . $displayPop . '%'; } elseif ($isPrecip) { $html .= 'N/A'; } else { $html .= '0%'; } $html .= ''; $colIndex++; } $html .= ''; $html .= ''; $colIndex = 0; foreach ($columns as $col) { $displayStyle = ($colIndex === 0) ? 'display: block;' : 'display: none;'; $targetId = 'desc-' . md5($zoneKey . $cleanCityName) . '-' . $colIndex; $html .= ''; $hasDay = !empty($col['day_desc']); $hasNight = !empty($col['night_desc']); if ($hasDay && $hasNight) { $html .= ' Day: ' . htmlspecialchars($col['day_desc']) . ''; $html .= ' Night: ' . htmlspecialchars($col['night_desc']) . ''; } elseif ($hasDay) { $html .= ' Day: ' . htmlspecialchars($col['day_desc']) . ''; } elseif ($hasNight) { $html .= ' Night: ' . htmlspecialchars($col['night_desc']) . ''; } else { $html .= '' . $col['day'] . ': ' . $col['condition'] . ''; } $html .= ''; $colIndex++; } $html .= ''; $html .= ''; } $html .= ''; return $html; } } if (!function_exists('sft_sort_ranked_zones')) { function sft_sort_ranked_zones($a, $b) { if ($a['is_ref'] && !$b['is_ref']) return -1; if (!$a['is_ref'] && $b['is_ref']) return 1; if ($a['distance'] == $b['distance']) return 0; return ($a['distance'] < $b['distance']) ? -1 : 1; } } if (!function_exists('sft_ucfirst_substr')) { function sft_ucfirst_substr($m) { return ucfirst(substr($m[1], 0, 3)); } } $cacheDir = '/home/wxworld/public_html/cachezone/sftcache/'; $primaryFile = $cacheDir . $cwaKey . '.json'; if (file_exists($primaryFile)) { $jsonContent = file_get_contents($primaryFile); $parsedPrimary = json_decode($jsonContent, true); if ($parsedPrimary && is_array($parsedPrimary)) { $parsedDataCollection = array($parsedPrimary); if (!empty($parsedPrimary['adjoining_cwas']) && is_array($parsedPrimary['adjoining_cwas'])) { foreach ($parsedPrimary['adjoining_cwas'] as $adjKey) { $adjFile = $cacheDir . strtolower(trim($adjKey)) . '.json'; if (file_exists($adjFile)) { $adjContent = file_get_contents($adjFile); $adjData = json_decode($adjContent, true); if ($adjData && is_array($adjData)) { $parsedDataCollection[] = $adjData; } } } } echo renderSftAreaForecast($parsedDataCollection, $stateCode); } else { echo 'Invalid JSON structure or decode failure in primary cache file.'; } } else { echo 'Source JSON cache file not found: ' . htmlspecialchars($primaryFile) . ''; } CODE%%

Trending Headlines

Trending Entertainment Headlines

Portal Radar Loop

Use the map search tool if you want to place a location marker on the radar map...

Portal Quick Facts

National Two Day Weather Forecast


E-mail the Portal Weather Forecast

Voice your opinion or tell us about page issues...