Pirate-Weather / pirateweather

Code and documentation for the Pirate Weather API
Apache License 2.0
660 stars 30 forks source link

Temperature min/max vs low/high #5

Closed trevorturk closed 7 months ago

trevorturk commented 2 years ago

Hello! 👋 I'm just familiarizing myself with your project and I noticed in your docs:

Dark Sky provides both temperatureHigh and temperatureMax values, and since I am not sure what the difference between them is, the same value is currently used for both.

Indeed, this is confusing! The best (only?) explanation I've seen is here: https://status.darksky.net/2017/08/29/daily-updates.html -- I'll quote it here in case they take down their blog:

A longstanding complaint with the Dark Sky API is that we report daily temperatures differently than other services: we report the minimum and maximum temperature during a given date, while other services report the daytime high and overnight low. (Put another way, our service reports temperature extrema between midnight and midnight, while other services report extrema from dawn to dawn.) These two were typically the same for daily high temperatures, but typically off by one day for the daily low temperatures (which usually occur around dawn). To address this complaint, we have added two new fields to daily data points: temperatureHigh and temperatureLow, which match the calculation methods used by other weather services. (temperatureMin and temperatureMax are still supported, but are now considered deprecated.)

Interestingly, the official Dark Sky iOS app seems to use the (allegedly deprecated) min/max instead of the new high/low.

Their documentation also aims to explain the differences between the data points (a bit too succinctly for my taste) but it makes sense once you have the context from their blog post. I'll quote it here as well:

temperatureHigh: The daytime high temperature. temperatureLow: The overnight low temperature. temperatureMax: The maximum temperature during a given date. temperatureMin: The minimum temperature during a given date.

To add some additional color, note that the AerisWeather API has a /forecast endpoint with "filters" which I found interesting in comparison: https://www.aerisweather.com/support/docs/api/reference/endpoints/forecasts/#filters -- so, I believe the including the mdnt2mdnt filter and then taking the minTemp/maxTemp would approximate the behavior of Dark Sky's min/max, for example.

As an aside, for a bit of background, this a point of contention with the more sophisticated users of my iOS weather app (https://helloweather.com) because the sources are inconsistent about when/what the overnight low temp is, and I really haven't found a great solution where all of the data sources we support are fully consistent. Instead, I tend to default to making our iOS app "look like" the data source's native iOS app. (Thus me noticing that Dark Sky's official app continues to use min/max even though they say it should be considered deprecated.)

Anyway, I'm not sure how this might impact your model (perhaps you'll choose to only support one or the other) but I figured I'd drop you a note since this confused me as well. Feel free to close this issue in any case, I wasn't quite sure where to post this info!

alexander0042 commented 2 years ago

Hi,

Thank you so much for opening this issue! From you follow I saw your app, and it's fantastic! Interface is gorgeous, and I love how it's so easy to compare sources. There's so much more to weather forecasting than is initially apparent, so I'm a fan of anything that says "this is the data", instead of trying to abstract all the sources away.

Don't know how you managed to turn up that source, but that's perfect. I'd found the data point descriptions, but without that blog post, they didn't make much sense. I agree, it's kind of a weird way to do it, but makes sense why they have two now.

At the moment (I think I've documented this), I use 4:00-4:00 (local) as bounds for these temperatures, since that's what was used for summaries and icons :

Summaries and icons on daily data point objects actually cover the period from 4AM to 4AM, rather than the stated time period of midnight to midnight. We found that the summaries so generated were less awkward.

However, this is clearer and I'm striving for compatibility here. Plus, since I've already got the time zone logic in there, it's an easy change to make. It's small enough that I'll work it into the v1.1.7 version, and I'll update this issue once that's live.

Thanks again for taking the time to look over this API and clarify this. Let me know if you notice anything else that's weird, or if you have any other suggestions!

trevorturk commented 2 years ago

That's great, thank you! I'm glad you like the Hello Weather app. It's been a side project for a few years and I've learned more about weather data than I intended when I initially set out... I'm sure you know the feeling!

I'm sorry I forgot to mention the 4am thing. It's funny, because in at least with AerisWeather they claim 7am is the "industry standard" (see the link I posted above) and IIRC one other source said something similar. In any case, for Pirate Weather it makes sense to match Dark Sky, of course.

I'm trying to implement Pirate Weather in Hello Weather now, using a new backend system I've been building that makes it easier to support multiple sources. (I hope to extract this into a product for general use -- I'll write you separately about that once I get a little further along on the tech side.) So, I have a pretty good test bed for comparing sources and ensuring we have equivalent data etc. The only other thing I noticed is that I believe you're returning times in UTC? I'm not 100% sure I have that right, but I'll be investigating more. If that's the case, I think that's worth changing. All of the other sources I use (and including Dark Sky) return epoch timestamps or datetime strings in the local timezone from what I can tell, so that stood out to me as a compatibility issue.

Oh, and I noticed your "units" param is "uk" as opposed to "uk2" in Dark Sky. Obviously not a big deal, but I figured I'm mention anything that I noticed as I started hooking into your API.

Thanks!

alexander0042 commented 2 years ago

I'd love to see Pirate Weather added in there, so definitely let me know how it goes!

For the times (I should clarify the docs here) everything is always returned in UTC. The local time zone is used to calculate the daily results, so I have that incorporated, but Dark Sky returned UTC, and I find it much easier to work with. The Dark Sky docs are a little confusing here:

time The UNIX time at which this data point begins. minutely data point are always aligned to the top of the minute, hourly data point objects to the top of the hour, daily data point objects to midnight of the day, and currently data point object to the point of time provided all according to the local time zone.

Which makes it seem like it'd be in local, but they mean that the response is the UTC time for midnight local. As a sanity check, I double checked this today and it lines up (for the record, it's raining here, so the win goes to PW): image image

A lot of packages automatically adjust for this though, so whatever you're using might convert it to local anyway.

Also, while uk2 is the official unit version, I'm just using the first two characters to select the system, so either will give you the same results (as would uk9). I might need to change this at some point, but until there's a need for a uk3, this works well enough. I am curious what the original uk was though...

trevorturk commented 2 years ago

Awesome, all sounds good. I'll let you know when we're ready to enable Pirate Weather and figure out the payment/donation stuff. For now I'm just trying to hook things up into our "multi source" architecture. That's great to hear the "rain" win coming for PW! I think DS seems like it's been gradually worsening over the last year or so. Who knows what they're up to now that they're part of Apple, maybe they're letting the API degrade, but incorporating stuff into Apple's operations more?

I'll take a closer look at the timestamp issue. I shouldn't have assumed it was a UTC issue, but I think something is amiss. Maybe the the timezone? I'll report back when I get to the bottom of that. Thanks again!

trevorturk commented 2 years ago

Ok, I think the problem may be the timezone library you're using. I'm comparing a request to DS and PW:

https://api.darksky.net/forecast/API_KEY/41.87,-87.62 -> "timezone": "America/Chicago" https://api.pirateweather.net/forecast/API_KEY/41.87,-87.62 -> "timezone": "America/Detroit"

This location is meant to be "the middle of Chicago" so it should have "America/Chicago" as the timezone: https://goo.gl/maps/TpymXu7CQQ75U2gP6

The epoch timestamps make sense if you take that into consideration, but this probably need to be fixed on your end or the data will come out strangely, if you're not picking the correct timezone for the 4am start etc?

alexander0042 commented 2 years ago

Ok, that explains it then!

That's actually a question I've been puzzling over for some time now. The issue is that looking up the exact time zone adds a little bit of time to requests, and for most applications it doesn't really matte, since the results are returned in UTC anyway, and at most it should be off by an hour. However, you're right that it's better for it to be correct, and I'm not convinced it was having as much of an impact as I thought it was, especially for warm starts of the Lambda instance.

If you want to try this yourself, there's another URL parameter that forces it to use the exact time zone location: tz=precise. This should solve any issues you're having on that front while I keep trying to optimize the response time in other ways.

trevorturk commented 2 years ago

Ok! Yeah, that seems to solve the issue for me. I'm not sure what to suggest on your end -- timezone lookup stuff is annoying. I was using https://github.com/zverok/wheretz but I think the data was becoming stale, but it seems to have been updated somewhat recently now. I'm using https://www.geonames.org now for some cases (with plenty of caching) -- that's one thing to consider is once you do a lookup, you can likely cache it for a while, eh?

alexander0042 commented 2 years ago

So annoying, and I've learned that the trick with this API is to optimize response times as much as possible- both because it's always better to return data faster, but also because when executions are in AWS Lambda, each millisecond shaved off saves some of the operational costs. Doesn't seem like much, but it adds up when it's multiplied by a million calls per week.

I'm using timezonefinder at the moment, and broadly speaking it works really well. it's up to date, entirely offline, and comfortably fits in the instance layer, so just a question of 0.1 seconds here or there,

trevorturk commented 2 years ago

Howdy! Sorry to pile onto this issue, and feel free to ignore this one, but I noticed another small deviation from the DS API. For precipType PW sometimes returns none where DS seems to only use rain and snow in my experience, and seems to default to rain instead of returning nil.

Their docs for reference:

precipType optional The type of precipitation occurring at the given time. If defined, this property will have one of the following values: "rain", "snow", or "sleet" (which refers to each of freezing rain, ice pellets, and “wintery mix”). (If precipIntensity is zero, then this property will not be defined. Additionally, due to the lack of data in our sources, historical precipType information is usually estimated, rather than observed.)

Perhaps PW could return rain, snow, or nil instead of none? I can handle this easily, so nbd, but I wanted to point it out since I noticed it!

alexander0042 commented 2 years ago

This one has been on my radar, and so I'm happy I'm kind of dubious about just skipping over fields, since it doesn't feel like a great solution; however, you are right that it's technically the way they do it. I really like the nil approach though, since looking at it again, that's much closer to their implementation.

One thing I'll preempt you on also relates to precipitation, and it's a much larger problem. Dark Sky defines precipitation accumulation only for snow, and always uses cm as the unit. I didn't realize this when I originally set things up, and it still seems unusual to ignore it for rain.

Clients sometimes implement around this by summing up hourly precipitations to get accumulation, but it feels like a clunky way to do it (for example, the home assistant integration, or this issue).

My current game plan here is to go with the Dark Sky implementation for compatibility, but add in a rainaccumulation field that provides the rain accumulation, leaving precipAccumulation for snow. This technically would break both DS compatibility and existing behavior, so I'll give a lot of heads up before implementing it, but I suspect it'll break fewer things by adding an extra field compared to the way precipAccumulation works now.

trevorturk commented 2 years ago

💯 these are some of the more confusing aspects of their API for sure. I have some old emails and GitHub issues I'll share for supporting documentation, but I thought daily precipAccumulation should only be used for snow, and I think it does (or at least it's supposed to) switch between in/cm. Also, the moon phase stuff is wacky, I'll share some details for that, too. I like your approach, though, and thank you for the links. Here's what I have currently, but I'd love to get to the bottom of things to ensure full compatibility:

Possible precipType values question: Gmail - precipType possible values?.pdf

I never heard back (I think they already sold to Apple at this point) but I believe I've only ever seen rain and snow.

Rail accumulation question: Gmail - Rain accumulation:amount?.pdf

Dark Sky docs:

precipAccumulation optional, only on hourly,currently and daily The amount of snowfall accumulation expected to occur (over the hour or day, respectively), in inches. (If no snowfall is expected, this property will not be defined.) precipIntensity optional The intensity (in inches of liquid water per hour) of precipitation occurring at the given time. This value is conditional on probability (that is, assuming any precipitation occurs at all). SI units are as follows: precipIntensity: Millimeters per hour. precipAccumulation: Centimeters.

So, in my app, I've been processing it like this:

I think that's all correct, but I'd love to get your take on things. Perhaps you talked to the Dark Sky folks about precipAccumulation always being cm -- that would be a major gotcha since their docs say it's in for us units -- or maybe you've just been able to determine how they're doing things by looking at the data? Issue Pirate-Weather/pirateweather#117 seems like you've paid close attention, so I'd welcome your advice.

FWIW the moonPhase is also strange, it seems to me like they're using the suncalc library, per: https://github.com/mourner/suncalc/issues/14 which isn't terribly precise, but (again) striving for compatibility for now...

From the DS docs:

moonPhase optional, only on daily The fractional part of the lunation number during the given day: a value of 0 corresponds to a new moon, 0.25 to a first quarter moon, 0.5 to a full moon, and 0.75 to a last quarter moon. (The ranges in between these represent waxing crescent, waxing gibbous, waning gibbous, and waning crescent moons, respectively.)

So, for help converting this into a string for display to customers, I emailed them: Gmail - Moon Phase question.pdf -- there's a calculation in there that's somewhat different from what the Tomorrow.io docs suggest here: https://docs.tomorrow.io/reference/data-layers-core:

0: New (0.0625-0.9375) 1: Waxing Crescent (0.0625-0.1875) 2: First Quarter (0.1875-0.3125) 3: Waxing Gibbous (0.3125-0.4375) 4: Full (0.4375-0.5625) 5: Waning Gibbous (0.5625-0.6875) 6: Third Quarter (0.6875-0.8125) 7: Waning Crescent (0.8125-0.9375)

...which is somewhat far off the Dark Sky calculations, but I haven't been able to find an authoritative formula to use.

Also in my testing, it seems like Dark Sky may be using the "end of the day" local timestamp to calculate the moon phase. So, I'm using a Ruby port: https://github.com/fishbrain/sun_calc and pulling out the phase like so: SunCalc.moon_illumination(daily.time.end_of_day).dig(:phase). I'm not feeling terribly confident about this, but trying to maintain compatibility for now...

trevorturk commented 2 years ago

Minor update -- I was digging through my old VCR cassettes (https://github.com/vcr/vcr) and found a Dark Sky response with precipType set to sleet -- apologies for missing that earlier.

rosenbergj commented 1 year ago

Hi! I just learned about this project in my search for a weather API to replace Dark Sky.

Perhaps unusually, I do rely on all 4 (well, 3 of the 4) of the temperature extrema data points in my application. My goal is to display the next high or low temperature, switching from a display of the high to a display of the low when the high is reached, and vice versa. (In the circumstance when the temperature is rising all evening and night, relatively uncommon but possible in the US mid-Atlantic, I want to print "n/a" between 6pm and midnight, switching to the next day's high at midnight. And vice versa for "temperature falling all day" and "between 6am and noon".) To do this, I also rely on the timestamps provided in Dark Sky for each of these, such as temperatureMinTime.

The notes I have for how to do this with Dark Sky are:

# Darksky logic:
#   Today's date is the right day to look at for next high/low, in all circumstances
#   Midnight to 6am, temperatureMin is a good low if in the future; else show high
#   6am to 6pm, temperatureHigh is a good high if in the future; else n/a in morning, show low in afternoon
#   6pm to midnight, temperatureLow is a good low if in the future, else n/a

I don't know if it's possible to do this in Pirate Weather (I am in the process of getting an API key), but it would be great if I can! None of the other APIs I've found handle this correctly in all of the various edge cases that my climate provides. I found this issue and wanted to provide more information to support the need for the distinct data points.

Thank you!

alexander0042 commented 1 year ago

This is honestly something I'd forgotten about, so thank you for bringing it to my attention again! The logic from Trevor's comment seems like it would work with your setup, which makes sense as it was how Dark Sky did it:

    temperatureHigh: The daytime high temperature.
    temperatureLow: The overnight low temperature.
    temperatureMax: The maximum temperature during a given date.
    temperatureMin: The minimum temperature during a given date.

What's tricky about this is what counts as overnight. Would it be from midnight to 6 am on a day and again from 6 pm to midnight on that day? Or from 6 pm the that day to 6 am on the next which seems more intuitive, but could also lead to a time in a daily block that isn't in that day, which I don't like (probably why it's depreciated).

However, my goal here is compatibility, so compatible it should be! Punching in London England, which is what I used for testing since there's no time zone, I'm seeing overnight times that stretch into the next day:

Day 1:

time: 1673654400
temperatureLowTime: 1673759220

Day 2:

time: 1673740800

Since 1673759220 is 5 am the following day, it looks like they're using that as 6 pm- 6 am as overnight.

Let me dig into this over the weekend and see what I can come up with, but I think I should be able to get it things working the same way

rosenbergj commented 1 year ago

Thank you, much appreciated!! Here are two examples that cover many of the edge cases I've found:

Example 1:

Example 1 desired behavior:

Example 2: (this was real weather in the US mid-atlantic this week! It is 10am on "day 1" as I'm writing this)

Example 2 desired behavior:

Thank you again!!

trevorturk commented 1 year ago

I'm not sure if this should be taken with a grain of salt, but I noticed more 4am-4am language here: https://darksky.net/dev/docs/faq -- but this is talking about icons... not sure if that applies to all data points:

How do you determine the value for the icon property on daily data points? Our system is presently very simple: it finds the “worst” weather condition that will happen during the day (4AM to 4AM), and uses the icon for it.

cloneofghosts commented 1 year ago

I know that DarkSky isn't very transparent on this but other APIs and websites that I've usually use 6am-6am as the period for high/low temp and if they offer min/max they use 12am-12am.

Since the overnight lows typically occur around 6am I think having high/low be from 6am-6am and having min/max being from 12am-12am makes the most sense in this situation.

alexander0042 commented 1 year ago

OK, now that I got alerts working I flipped back to this! This was a combination of bug + logic issue, but I just released v1.3 which addresses both. I'm updating the docs now, but this is the setup:

    temperatureHigh: The daytime high temperature (6:00 am to 6:00 pm).
    temperatureLow: The overnight low temperature (6:00 pm to 6:00 am on the next day).
    temperatureMax: The maximum temperature during a given date (12:00 to 12:00).
    temperatureMin: The minimum temperature during a given date (12:00 to 12:00).

As far as I can tell, this aligns with how Dark Sky did it, which is always the goal. I'm still using 4:00 to 4:00 as the period for the icon and text summary, but otherwise everything is on the expected 12:00 to 12:00 frame.

Let me know if this matches the expected behavior on your end or if I need any additional tweaking here.

rosenbergj commented 1 year ago

Thank you for working on this so quickly! Right now I'm seeing these differences at location (39.95,-75.17):

Dark Sky:

Pirate Weather:

This particular time doesn't have a practical impact on my project (I only look at temperatureMin between midnight and 6am), but it's a material difference that I wanted to point out. Does Pirate Weather ever report high/low/min/max in the past? If not, then it will indeed continue to have occasional big differences from Dark Sky in these values.

alexander0042 commented 1 year ago

Thanks for testing this so quickly- you checked it out before I even had a chance to update the docs!

Your assumption here is correct, PW will always return the forward looking values, which is a difference from how Dark Sky did it. Unfortunately, this is a vary hard to fix issue with the setup. Since everything is file based, the API doesn't know anything about what happened in the past, so can only return values looking forward, and I would have to add a bunch of file system reads in order to address it.

The one way around this is to use the short-term time-machine functionality to request the forecast as it was at midnight. This means you wouldn't get the most up to date data, but since it's reading previous files, everything would be calculated for the entire day. So something like: https://api.pirateweather.net/forecast/<API_KEY>/39.95,-75.17,1674536400 would give you the forecast from today at 12:00 am, ensuring that all the times are in the future

cloneofghosts commented 1 year ago

@alexander0042 It looks like the high/low temperatures are being calculated from 4am-4am instead of the stated 6am-6am. Looking at the daily block it shows a low temperature time of Wednesday, February 1, 2023 4:00:00 AM GMT-05:00

But looking through the hourly blocks I see that at Wednesday, February 1, 2023 5:00:00 AM GMT-05:00 there is a lower temperature than what the daily block shows. I have the daily block and the hourly block from the API shown below with just the relevant information.

Daily:

        "time": 1675141200,
        "temperatureHigh": -8.96,
        "temperatureHighTime": 1675195200,
        "temperatureLow": -26.65,
        "temperatureLowTime": 1675242000,
      },

Hourly:

      {
        "time": 1675245600,
        "temperature": -27.21,
      },

Is it supposed to be calculating from 4am-4am or 6am-6am? The documentation and the response you gave earlier state it should be using 6am-6am. So either this is a bug or the documentation should be changed to state it's being calculated from 4am-4am.

alexander0042 commented 1 year ago

I totally understand why Dark Sky decided to depreciate the TemperatureHigh/ TemperatureLow parameters now, they're not at all intuitive, and really are a pain to work with. This is a good catch though, since this is a bug. TemperatureLow should be from 6 pm to 6 am, so the fact that it's not picking up the 5 am value is an issue. I'm 90% sure it's an "off by one" problem, so that's where I'll start.

More broadly here, I was thinking about what @rosenbergj's comment a lot, and I really don't like the answer I came up with. It really isn't too tricky for me to get the values for earlier in the current day. I realized that since this is only for temperature, that's the only value I'd need to read, so honestly pretty doable. I'm going to try to implement this over the weekend and see how it goes,

cloneofghosts commented 1 year ago

@alexander0042 When the temperatures rise throughout the day it's possible to have a low temperature that is higher then the high temperature. There isn't anything wrong with your current setup and maybe the easiest solution would be to swap the values if the high temp is lower then the low temp or you could just leave as is? Not sure what would be the best solution for this quirk.

        "time": 1675486800,
        "icon": "snow",
        "summary": "Snow",
        "sunriseTime": 1675513212,
        "sunsetTime": 1675548836,
        "moonPhase": 0.46524949386685727,
        "precipIntensity": 0.0704,
        "precipIntensityMax": 0.1711,
        "precipIntensityMaxTime": 1675566000,
        "precipProbability": 0.172,
        "precipAccumulation": 0.8453,
        "precipType": "snow",
        "temperatureHigh": -16.27,
        "temperatureHighTime": 1675544400,
        "temperatureLow": -14.59,
        "temperatureLowTime": 1675551600,
        "apparentTemperatureHigh": -18.03,
        "apparentTemperatureHighTime": 1675544400,
        "apparentTemperatureLow": -34.86,
        "apparentTemperatureLowTime": 1675558800,
        "dewPoint": -20.62,
        "humidity": 0.747,
        "pressure": 1016.4,
        "windSpeed": 12.36,
        "windGust": 26.49,
        "windGustTime": 1675537200,
        "windBearing": 166.95,
        "cloudCover": 0.991,
        "uvIndex": 2.57,
        "uvIndexTime": 1675533600,
        "visibility": 16.09,
        "temperatureMin": -25.52,
        "temperatureMinTime": 1675526400,
        "temperatureMax": -13.27,
        "temperatureMaxTime": 1675566000,
        "apparentTemperatureMin": -34.86,
        "apparentTemperatureMinTime": 1675526400,
        "apparentTemperatureMax": -18.03,
        "apparentTemperatureMaxTime": 1675566000

I think the same "off by one" issue exists for the high temperature since it's showing a high time of 4pm today instead of 5pm.

rosenbergj commented 1 year ago

When the temperatures rise throughout the day it's possible to have a low temperature that is higher then the high temperature.

I think it should be left as-is. The high and low represent different non-overlapping time periods, so it's consistent that this might happen sometimes. The code using the API can take this into account by loooking at Max and Min, for example.

cloneofghosts commented 1 year ago

I understand that from the way its been setup high/low temperatures are for different time ranges and you can always look at min/max temperatures if needed.

I checked around on other websites to see how they handle this situation and most seem to try and have a high temperature that is higher then the low temperature outside of Environment Canada where it showed a low temperature higher then the high temperature. It doesn't bother me either way but I posted about it in case others have a preference one way or the other.

Monstergerm commented 1 year ago

I think there is another issue with the forecasted daily0 temperatureHigh.

I expect this to be a forecast of the expected temperature High for today that stays the same during the forecast period (today or 4 am to 4 am whatever it is). Perhaps it is acceptable to increase the value of forecasted temperature if during the day the actual temperature increases above this value.

However, what I found is that the daily0 temperatureHigh constantly changes throughout the day and in particular in the afternoon seems to track the current temperature and will get lower and lower. In other words, if the forecast value for daily0 temperatureHigh was 65 F and this value was reached at 1 pm, I now notice during the afternoon daily0 temperatureHigh values of e.g. 60 F at 4 pm, which is the exact current temperature at 4 pm and it is confirmed by the timestamp for temperatureHighTime, which also is 4 pm and no longer 1 pm. I expect daily0 temperatureHigh to stay fixed at 65 F until the next forecast period starts. It should not be the temperature High from the current time until end of forecast period.

github-actions[bot] commented 1 year ago

There has not been any activity on this issue in the last ninety and will automatically close in seven days. Comment on this issue to prevent this issue from closing automatically.

cloneofghosts commented 7 months ago

This issue has been fixed in the V2 development API which you can access using the dev endpoint.

alexander0042 commented 7 months ago

Filed under "it seemed so much easier than it was", but 2 years later we're here. The overriding issue was that the API always took the most recent model results as the starting point; however. to do this correctly you need to know what happened over the previous (up to) 24 hours, so the data just wasn't there. Complicating things was that I didn't want to use forecast results for times that had already happened, which reduces accuracy, and minimize read calls, since that slows it down. Version 2 fixes all this by reading in the previous 36 hours of observations when making a new forecast file, which gives the API access to that data directly!