5
\$\begingroup\$

I'm using the openweathermap API to get weather forecast data. I want to make sure that the forecast values are at minimum 2 hours in the future. The API is providing the forecast in 3 hour steps, so I need to make sure that the weather data are in the future.

Here is an example response provided by openweathermap API:

{"city":{"id":1851632,"name":"Shuzenji",
"coord":{"lon":138.933334,"lat":34.966671},
"country":"JP",
"cod":"200",
"message":0.0045,
"cnt":38,
"list":[{
        "dt":1406106000,
        "main":{
            "temp":298.77,
            "temp_min":298.77,
            "temp_max":298.774,
            "pressure":1005.93,
            "sea_level":1018.18,
            "grnd_level":1005.93,
            "humidity":87
            "temp_kf":0.26},
        "weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],
        "clouds":{"all":88},
        "wind":{"speed":5.71,"deg":229.501},
        "sys":{"pod":"d"},
        "dt_txt":"2014-07-23 09:00:00"}
        ]}

I'm using JSON. The list property has the value dt_txt in this format: year-month-day 09:00:00. Now I need to check if this DateTime is minimum 2 hours in the future and if not I simply take the item at index 1 of the list.

I wrote following code:

 // look for next prediction
var weatherList = parsed.list;

// Check which time is relevant
// Get only time without description or/and date
// 2017-03-17 21:00:00
// ~~~~~~~~~~~~~^
var columnIndex = weatherList[0]["dt_txt"].indexOf(":") - 2;
// 2017-03-17 21:00:00
// ~~~~~~~~~~~^
var apiHoursString  = weatherList[0]["dt_txt"].substring(columnIndex, columnIndex + 2);

// server time:
var hours = String((new Date()).getHours());
if(hours.length < 2){
    hours = "0" + hours;
}

var apiHoursInt = parseInt(apiHoursString);
var hoursInt = parseInt(hours);

if((apiHoursInt - hoursInt) < 2){
    indexToUse = 1
}

I know this code is bad - what is the proper way to do that on Node.js?

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

I would not parse the dt_txt field. You would have to make too many assumptions including the format of the date (Is it yyyy-mm-dd or the American mm-dd-yyyy) and the time zone. Instead create a date object from the dt field:

var apiHours = (new Date(weatherList[0].dt * 1000)).getHours();

All those operations to add 0 and do hours to string to integer conversions are redundant. The following should be sufficient:

let weatherList  = parsed.list;
let apiHours     = (new Date(weatherList[0].dt * 1000).getHours();
let serverHours  = (new Date()).getHours();
let indexToUse   = (apiHours - serverHours < 2) ? 1 : 0;

Update 1

David is right, I had missed that point. A better solution would be:

// All values in milliseconds
const oneHour   = 60 * 60 * 1000;
let  apiTime    = weatherList[0].dt * 1000;
let  serverTime = (new Date()).valueOf();
let  indexToUse = (apiTime - serverTime < 2 * oneHour) ? 1 : 0;
\$\endgroup\$
3
  • \$\begingroup\$ What is that line: (new Date(weatherList[0].dt * 1000).getHours(); - especially multiplying it with 1000 - doing? \$\endgroup\$ Mar 20, 2017 at 18:34
  • 1
    \$\begingroup\$ The 'dt' field in your result is the number of seconds since the Unix epoch. I multiplied that by 1000 to convert it to milliseconds and used it to create a date object representing the time of the forecast. \$\endgroup\$ Mar 21, 2017 at 0:00
  • \$\begingroup\$ Won't this fail if the values straddle midnight? apiHours = 2, serverHours = 23, apiHours - serverHours < 2 === true so it skips the first value even though it's in the future. It may also get tricked up by daylight savings time. How about just comparing the raw timestamps which are monotonic increasing? \$\endgroup\$ Mar 22, 2017 at 1:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.