Calculate age in years, months and days

There are several reasons, off the top of my head:

  • DOpusFactory dates support initializing from ISO date strings which simplify the user input for you.
  • Opus itself will give you DOpusFactory date objects everywhere (e.g., when working with metadata etc. situations you had in your forum topics) so you might as well use them exclusively to avoid mixing different API (e.g., x.year vs. x.getFullYear() etc).
  • There is no need to handle month offset when using DOpusFactory dates.
  • The way JScript dates handle the time zones is inconvenient.
  • etc.

What error exactly? Did you forget to add the getMillis function? I don't see it in your full code above.

I imagined the same thing, that DOpusFactory would be more versatile, more single, more customizable.

I really forgot to add the getMillis function, sorry.

I added the getMillis function and now it gives me an error in:

// Mostrar el resultadot
var fecha1 = fecha1.Format("D#dd-MM-yyyy");
var fecha2 = fecha2.Format("D#dd-MM-yyyy");

Ignoring them, the result is the following:

2023-08-16 13 28 38

How can I format fecha1? The result is showing the number of milliseconds, not the date itself.

function OnClick(clickData)
{
	var DOpusFactory = DOpus.Create();
	var dlg = clickData.func.Dlg;

	function getMillis(opusDate) {
		return new Date(opusDate.year, opusDate.month - 1, opusDate.day).getTime();
	}

	//Fecha de nacimiento
	var fecha1 = DOpus.Create().Date("2017-02-13");
	var fecha1 = getMillis(fecha1);

	//Fecha de cálculo (sugiriéndose como fecha, la fecha actual
	var fecha2 = dlg.getString("Fecha de cálculo (DD-MM-AAAA):", DOpusFactory.Date().Format("D#dd-MM-yyyy")); // Se sugiere la fecha actual
	if (!fecha2) {
		return; // Si el usuario cancela o no se introduce nada, salir
	}
	fecha2 = DOpusFactory.Date(fecha2);

	//Calcula edad	
	var diff = getMillis(fecha2) - getMillis(fecha1);

	//Mensaje de error si fecha menor que 0
	if(diff <0){
	var fecha1 = fecha1.Format("D#dd-MM-yyyy");
	var fecha2 = fecha2.Format("D#dd-MM-yyyy");
	dlg.title = "Edad Sophia para...";
	dlg.message = "Error: Edad de cálculo tiene que ser mayor que fecha de nacimiento.\n\nFecha nacimiento: " + fecha1 + "\nFecha de cálculo:  " + fecha2;
	dlg.icon = "error";
	dlg.buttons = "Aceptar";
	dlg.Show();
	return;	
    }

    var totalDias = Math.floor(diff / 8.64e+7);

    var anios = fecha2.year - fecha1.year;
    var meses = fecha2.month - fecha1.month;
    var dias = fecha2.day - fecha1.day;

    if(dias<0){
      var primerDiaProximoMes = DOpusFactory.Date(fecha1.year, fecha1.month+1, 1);
      var diff = primerDiaProximoMes - fecha1;
      var diasHastaFinMes = Math.floor(diff /  (60 * 60 * 24 * 1000));
      dias = diasHastaFinMes + getMillis(fecha2) - 1;
      meses--;
    }

    if(meses<0){
      meses = 12 + meses;
      anios--;
    }
	
	//Ignorar valores 0
    var strAnios = (anios != 0) ? (anios + " años, ") : "";
    var strMeses = (meses != 0) ? (meses + " meses, ") : "";
    var strDias = (dias != 0) ? (dias + " días ") : "";
    var strTotal = (strAnios + strMeses + strDias).slice(0,-1);

    var edad = "" + (strTotal || "0 tiempo") + "";
	  
	// Mostrar el resultado
	//var fecha1 = fecha1.Format("D#dd-MM-yyyy");
	//var fecha2 = fecha2.Format("D#dd-MM-yyyy");
	
	dlg.title = "Edad Sophia para la fecha";
	dlg.message = "Fecha nacimiento: " + fecha1 + "\nFecha de cálculo:  " + fecha2 + "\n\nEdad: " + edad;
	dlg.icon = "info";
	dlg.buttons = "Aceptar";
	dlg.Show();
}

Delete the line 12 from this code and it will work.

I recommend getting some extra sleep because there may be more problems in your code for other dates :smiley:

What a disappointment, oh cruel life :grinning:

1 Like

Hi @bytespiller , me again. :grinning:

I wanted to ask you something. How could your code be adapted to also consider values =1? For example, 1 years > 1 year. I try to decipher the syntax of those instructions but I can't, are they conditions without "if", without "else"?

var strAnios = (anios != 0) ? (anios + " años, ") : "";
var strMeses = (meses != 0) ? (meses + " meses, ") : "";
var strDias = (dias != 0) ? (dias + " días ") : "";
var strTotal = (strAnios + strMeses + strDias).slice(0,-1);

I've tried with traditional conditions, but it's a lot of code

if (anios > 1);{
   var strAnios = anios + " años;
   } else if (Anios = 1) {
   var strAnios = anios + " año;
   } else {
   var strAnios = " ";
}

? is called the conditional or ternary operator.

x = a ? b : c;

is equivalent to

if (a) x = b; else x = c;.

You can string more than one test together, so e.g.:

x = a ? b : (c ? d : e);

is equivalent to:

if (a) x = b;
else if (c) x = d;
else x = e;
1 Like

Perfect @Jon , thank you very much!

I created the following conditions, and when I give the value 0 to the variables, they are displayed when they should not be displayed, and to complicate things more, the value displayed is 1 and not 0. Can someone help me?!?

function OnClick(clickData) {
    var dlg = clickData.func.Dlg();

	var anios = 5
	var meses = 0
	var dias = 0

	var strAnios = (anios > 1) ? (anios + " años, ") : (anios = 1) ? (anios + " año, ") : "";
	var strMeses = (meses > 1) ? (meses + " meses, ") : (meses = 1) ? (meses + " mes, ") : "";
	var strDias = (dias > 1) ? (dias + " días") : (dias = 1) ? (dias + " día") : "";

	var edad = (strAnios + strMeses + strDias).slice(0,-1);

    dlg.title = 'Edad';
    dlg.message = edad;
    dlg.buttons = '&Aceptar';
    dlg.show
}

01

I don't recommend using too complex ternary expressions (i.e., with more than one ? and :) as the code then becomes difficult to parse (by humans I mean).

If there are multiple complex things which follow the same logic, you can make a function to do the work for you.

var SEPARATOR = ", ";

function que(value, singular, plural) {
	if (!value) {
		return "";
	}

	var suffix = (Math.abs(value) > 1) ? plural : singular;
	return value + " " + suffix + SEPARATOR;
}

function OnClick(clickData) {
	var dlg = clickData.func.Dlg();

	var anios = 5
	var meses = 0
	var dias = 0

	var strAnios = que(anios, "año", "años");
	var strMeses = que(meses, "mes", "meses");
	var strDias = que(dias, "día", "días");
	
	var strTotal = (strAnios + strMeses + strDias).slice(0, -SEPARATOR.length);	
	var edad = strTotal || "0 tiempo";
	
	dlg.title = 'Edad';
	dlg.message = edad;
	dlg.buttons = '&Aceptar';
	dlg.show();
}
1 Like

Thank you very much once again @bytespiller, how nice it is to know how you know. It doesn't have to be now, but when I have time, explain to me the behavior of the line var edad = strTotal || "0 tiempo"; please. I identify two strings separated by the logical operator OU ( || ), so it must be a condition, but I don't know the decision criteria to incorporate the string "0 tiempo" to the result

You need to use == to test for equality, = by itself does assignment.

2 Likes

I understood, I had forgotten that detail, thank you very much @Jon

It's an OR condition equivalent to either of these (all give the same result):
var edad = (strTotal == "") ? "0 tiempo" : strTotal; or
var edad = (strTotal) ? strTotal : "0 tiempo"; or
var edad = (!strTotal) ? "0 tiempo" : strTotal; or
var edad = (strTotal.length > 0) ? strTotal : "0 tiempo"; or
var edad = (!strTotal.length) ? "0 tiempo" : strTotal; or
etc.

All of these can be written in traditional if forms such as:

var edad = undefined;
if (strTotal == "") {
    edad = "0 tiempo";
}
else {
    edad = strTotal;
}

etc.

To understand this better, see the concept of truthy and falsy in JavaScript (different from true and false). Please read this now, or you won't understand anything since the JavaScript is a weird language.

So in var edad = strTotal || "0 tiempo"; if the strTotal is an empty string JavaScript sees it as a logical false (due to falsy evaluation), and will then select the "0 tiempo" which is not an empty string and is therefore logical true (due to truthy evaluation)

See the concept of short circuiting in JavaScript to understand the logic behind it.

And additionally since the JavaScript is funky like that, the operator === actually truly means equality check, while the == makes the JavaScript try to interpret the values in a way to make it evaluate to true if at all possible (also known as "truthy" mentioned above). That is why it is recommended to use the === (and !== for negation) operator, except in situations where you want to exploit the fuzzy == "truthy/falsy" behavior.

Example:

  • 0 == false as well as "0" == false evaluates to true (because JS makes the implicit type coercion)
  • 0 === false as well as "0" === false evaluates to false (because 0 (or "0") and false are different types)
2 Likes

Oh @bytespiller, as always, thank you very, very much, I don't know what your profession is, but I know you have the soul of a teacher, I perfectly understood everything you explained to me, and even so, I read the articles you indicated, the amount is impressive of resources that exist in the fascinating world of programming.

I am happy with the things that I am learning day by day, yesterday I learned about the ternary operator and today about the == and === operators, so they are not the same thing, for example:

('1' == 1); //True
('1' === 1); //False

Thank you, thank you, thank you!!!

Hi @bytespiller , I updated the code with its new conditions and it's getting more and more beautiful. But it continues giving errors in some dates as you told me, mainly in those in which the day of the date of birth is greater than the day of the current date, example: 25 and 20 respectively. On these dates the number of days calculated appears to be in milliseconds.

2023-08-20 15 35 36

I am very sorry to ask you this, but you could help me to write a code that works well. No matter how hard I study and try to do it myself, I can't do it. :anguished:

function OnClick(clickData)
{
	var dlg = clickData.func.Dlg;
	var DOpusFactory = DOpus.Create();

	function getMillis(opusDate) {
		return new Date(opusDate.year, opusDate.month - 1, opusDate.day).getTime();
	}

	//Fecha nacimiento
	var fecha1 = dlg.getString("Fecha nacimiento (DD-MM-AAAA):");
	if (!fecha1) {
		return;
	}
	fecha1 = DOpusFactory.Date(fecha1);

	// Fecha actual
	var fecha2 = DOpusFactory.Date().Format("D#dd-MM-yyyy");
	if (!fecha2) {
		return;
	}
	fecha2 = DOpusFactory.Date(fecha2);

	//Calcular tiempo	
    var diff = getMillis(fecha2) - getMillis(fecha1); //Tiempo en milisegundos
	var totalDias = Math.floor(diff / 8.64e+7); //Tiempo en días

	//Mensaje de error si fecha1 > fecha2
	if(diff < 0){
	var fecha1 = fecha1.Format("D#dd-MM-yyyy");
	var fecha2 = fecha2.Format("D#dd-MM-yyyy");
	dlg.title = "Edad actual";
	dlg.message = "Error: La fecha de nacimiento tiene que ser menor que la fecha actual.\n\nFecha nacimiento: " + fecha1 + "\nFecha actual:        " + fecha2;
	dlg.icon = "error";
	dlg.buttons = "Aceptar";
	dlg.Show();
	return;	
    }
	
	//Calcular edad	
    var anios = fecha2.year - fecha1.year;
    var meses = fecha2.month - fecha1.month;
    var dias = fecha2.day - fecha1.day;

    if(dias<0){
      var first_day_next_month = DOpusFactory.Date(fecha1.year, fecha1.month+1, 1);
      var diff = first_day_next_month - fecha1;
      var days_until_end_month = Math.floor(diff /  (60 * 60 * 24 * 1000));
      dias = days_until_end_month + getMillis(fecha2) - 1;
      meses--;
    }

    if(meses<0){
      meses = 12 + meses;
      anios--;
    }
	
	//Ignorar valores 0
	var SEPARATOR = ", ";
	function que(value, singular, plural) {
		if (!value) {
			return "";
		}
		var suffix = (Math.abs(value) > 1) ? plural : singular;
		return value + " " + suffix + SEPARATOR;
	}

	var strAnios = que(anios, "año", "años");
	var strMeses = que(meses, "mes", "meses");
	var strDias = que(dias, "día", "días");
	
	var strTotal = (strAnios + strMeses + strDias).slice(0, -SEPARATOR.length);	
	var edad = strTotal || "0 edad";
	  
	// Mostrar el resultado
	var fecha1 = fecha1.Format("D#dd-MM-yyyy");
	var fecha2 = fecha2.Format("D#dd-MM-yyyy");
	
	dlg.title = "Edad actual";
	dlg.message = "Fecha nacimiento: " + fecha1 + "\nFecha actual:        " + fecha2 + "\n\nEdad: " + edad;
	dlg.icon = "info";
	dlg.buttons = "Aceptar";
	dlg.Show();
}

The problem is in if(dias<0) block:

  1. You forgot to use the getMillis in var diff = first_day_next_month - fecha1; + I think you may need to swap the order of operations (fecha1 - first_day.... instead), so check that too.
  2. The dias = days_until_end_month + getMillis(fecha2) - 1; seems wrong to use getMillis(fecha2), I think you wanted fecha2.day?

I can't help with math though, that's not my strong suit :slight_smile:

Ok @bytespiller , I'm going to try to correct what you tell me and then I'll tell you, thanks a thousand times once again!!!

I made the corrections you indicated and the number of days is now expressed as a smaller number. 6 days is expressing it as 42068, strange, it is neither milliseconds, nor seconds, nor minutes, nor hours. :thinking:

2023-08-21 05 15 16

Hi @bytespiller, I'm trying to rewrite the code in pure JScript format, without any OpusFactory, to see if I understand things better, but I'm having a problem, I enter the date of birth, either in yyyy-MM-dd or in dd format -MM-yyyy, and the result is the same, the date used to perform the calculation is quite different from the one specified.

How could I convert that string to a new Date object? I have tried but the result is the famous NaN.

	var fecha1 = dlg.getString("Fecha nacimiento (DD-MM-AAAA):");
	if (!fecha1) {
		return;
	}

	var anioF1 = fecha1.slice(-4);
	var mesF1 = fecha1.slice(-7, 2);
	var diaF1 = fecha1.slice(0, 2);
	var strF1= anioF1 + "-" + mesF1 + "-" + diaF1;

    var fecha1 = new Date(strF1);