0

I've made a basic weather app by using openweathermap free API, everything works fine in development and production versions, both in my PC, but when I go to my iPhone, the part of the data that depends on javascript Date API, isn't working, I just get Nan or "Invalid Number" (I put that in some functions I made). I've made a few functions to help me into formatting the incoming JSON data, but when I receive text for the city name, or numbers for the temperature, everything works fine.

This is how the data is being shown in my iPhone:

Wrong data in Safari for iOS

I'm using the useEffect hook in react to fetch the data, and there I also format the data:

            fetch(`https://api.openweathermap.org/data/2.5/forecast?id=3590979&appid=${key}&units=${units}&lang=${languaje}`)
            .then((response) => {
                return response.json();
            })
            .then((data) => {
                const [cityData, listData] = Clean5DaysForecastData(data, 'C', languaje);
                setCity(cityData);
                setList(listData);
            });

This is my formatting data function:

export function Clean5DaysForecastData(data, tempUnit = 'C', lang = 'en', formattingCityFunction = formattedCity) {
    const city = formattingCityFunction(data.city);
    const dataLength = data.list.length;
    const filteredList = [];
    for (let index = 0; index < dataLength; index += 8) {
        const singleData = {};
        singleData['description'] = firstCharToUpper(data.list[index].weather[0].description);
        singleData['temperature'] = formattedTemperature(data.list[index].main.temp, tempUnit);
        singleData['dateTime'] = formattedDateTime(data.list[index].dt_txt, lang);
        singleData['weekDay'] = formattedWeekDay(data.list[index].dt_txt, lang);
        singleData['icon'] = data.list[index].weather[0].icon;
        filteredList.push(singleData);
    }
    return [city, filteredList];
}

This are my helper functions:

import { monthName, weekdays } from "./DateStrings";
function formattedDateTime(dateTimeText = '', lang = 'en', formattingMonthFunction = monthName) {
    const date = new Date(dateTimeText);
    const month = formattingMonthFunction(date.getMonth(), lang);
    const day = date.getDate();
    const hours = date.getHours();
    const minutes = date.getMinutes();
    return `${month} ${day}, ${hours === 0 ? '00' : hours}:${minutes < 10 ? '0' + minutes : minutes}`;
}

function formattedWeekDay(dateTimeText = '', lang = 'en', formattingFunction = weekdays) {
    const date = new Date(dateTimeText);
    return formattingFunction(date.getDay(), lang);
}

function firstCharToUpper(dataString = '') {
    return dataString.charAt(0).toUpperCase() + dataString.slice(1);
}

function formattedTemperature(temp = 0, tempUnit = 'C') {
    return `${temp} °${tempUnit}`;
}

function formattedCity(data = '') {
    return data.name + ', ' + data.country;
}

And this are my Month and Weekdays functions, where I convert the numbers into text:

import { monthNameLocales, weekdaysLocales } from "./LocaleStrings";
export function weekdays(day, locale = 'en', weekDaysStrings = weekdaysLocales) {
    if (day >= 0 && day < 7) {
        if (weekDaysStrings[locale][day]) {
            return weekDaysStrings[locale][day];
        }
        else {
            return 'Invalid languaje';
        }
    }
    else {
        return 'Inalid day number';
    }
};

export function monthName(month, locale = 'en', monthNameStrings = monthNameLocales) {
    if (month >= 0 && month < 12) {
        if (monthNameStrings[locale][month]) {
            return monthNameStrings[locale][month];
        }
        else {
            return 'Invalid languaje';
        }
    }
    else {
        return 'Invalid month number';
    }
}

They depend in some associative arrays which are not relevant, because everything is working in my PC.

Edit: If somebody is interested, this is my object with associative arrays:

export const weekdaysLocales = {
    'en': ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
    'es': ['Domingo', 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado']
}

export const monthNameLocales = {
    'en': ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
    'es': ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre']
}
elG
  • 539
  • 1
  • 6
  • 20
  • Looks like you are calling formattingMonthFunction and formattingFunction instead of weekdays() and monthName(). Clearly your else is getting called in weekdays(). Can you add those missing functions as well? – Sanish Joseph Aug 29 '21 at 06:13
  • @SanishJoseph I'm adding a default value to my functions parameters, as you can see in the formattedWeekday function, there's this parameter `formattingFunction = weekdays`, that means that I'm sending a function as a parameter with it's default value, that's to prevent coupling in my functions, so that parameter function can be changed anywhere I want without having to refactor formattedWeekday function's code. So, formattingFunction is the same as weekDays. Thank you for your observation, but I think it's more of a Safari problem, because in my PC, also in Android it's working really well. – elG Aug 30 '21 at 03:15
  • 1
    I believe you don't have any option to add a `debugger;` and see what's going on in your iPhone. My idea is to check if `const date = new Date(dateTimeText); ` gets the value correctly. – Sanish Joseph Aug 30 '21 at 03:22
  • 1
    If you don't have any debugging options for iPhone, try https://browserstack.com which gives options to debug a real iPhone with browsers. – Sanish Joseph Aug 30 '21 at 03:33

2 Answers2

1

How I understand in your response date what you get is unsupported date format for React, I would suggest you to use in-built methods to display month and week day, because what you do with arrays it is not good in practice, this is my version how I would do that

   let responseDate = '2021-08-30 09:00:00';

   //splitting string in date and time
   let splitData = responseDate.split(" ")

   //returning array to string
   const s = splitData[0].toString()

   // Split on non–digit character
   let [y, m, d] = s.split(/\D/);

   // Use the Date constructor
   let date = new Date(y, m - 1, d);

   console.log(date.toDateString());
   console.log(date.toLocaleString('default',{weekday:'long'}));
   console.log(date.toLocaleString('default', { month: 'long' }));
   console.log(splitData[1])
callmenikk
  • 1,358
  • 2
  • 9
  • 24
  • 1
    Thank you so much, I found a solution following my approach, but I think yours is better, because the dateTime format is the same I'm getting from the API, also it reduces the lines of code I'm using. – elG Aug 31 '21 at 00:49
1

Thanks to Sanish Joseph and callmenikk. With Sanish Jospeh's answer, I tried to use browserstack, but the free account just gave me a short period of time to debug, so I tried this this code instead in my formattedDateTime function:

return dateTimeText + ' / ' + date + ' // ' + date.getMonth() + ' ' + day + ', ' + hours + ':' + minutes;

With that, I found that "date" was giving me and "invalid date", it was close to callmenikk answer, so I looked for "unsupported date format for safari OS", and I found this previous question:

Invalid date in safari

Then I remembered that openweathermap API JSON response had two datetime properties, the first one is dt_text, which I used, and it's format was yyyy-mm-dd hh:mm:ss, this is a non standard datetime format according to the previous Stack Overflow question, it's supported by some browsers, but safari for iOS isn't one of them, so I used dt data field instead, which was in miliseconds, I changed this part in the Clean5DaysForecastData function:

        singleData['dateTime'] = formattedDateTime(data.list[index].dt * 1000, lang);
        singleData['weekDay'] = formattedWeekDay(data.list[index].dt * 1000, lang);

If you look closely, instead of having ...[index].dt_txt, now I have ...[index].dt * 1000.

Another solution is using a third party library, like DayJS or Luxon, because they support non standard datetime string formats, but there's a small performance cost by using this libraries.

Edit: I marked callmenikk answer as a solution because his approach is cleaner, by using the same dateTime format I was getting from the API at first.

elG
  • 539
  • 1
  • 6
  • 20