Relay delay on and delay off - in this a good way to do it?

Relay delay on and delay off - in this a good way to do it? It works but is there a more elegant solution?

/* energise relay after delay, with delay off
   input - switch pulling low
*/

// setup pin numbers
int pinRelay = 13; // relay
int pinInput = 0; // button

// delays before relays turn on & off
const unsigned int delayOnTimeRelay = 1000;
const unsigned int delayOffTimeRelay = 2000;

// variables for delay timers
unsigned long timerRelay = 0;

// boolean variables
boolean input; // some input
boolean delayOnRelay; // on timer started
boolean delayOffRelay;// off timer started
boolean relay; 


void setup() {

  pinMode(pinRelay, OUTPUT); // relay
  pinMode(pinInput, INPUT_PULLUP);


  Serial.begin(9600);

}

void loop() {

  input = digitalRead(pinInput);

  // example controlling relay, not real world:
  if (input == 0) {
    relay = 1;
  }
  else {
    relay = 0;
  }

  //turn relay on after delay
  if (relay == 1 && delayOnRelay == 0 && digitalRead(pinRelay) == LOW) {
    timerRelay = millis();
    delayOnRelay = 1;
  }
  if (delayOnRelay == 1 && (millis() - timerRelay) > delayOnTimeRelay) {
    delayOnRelay = 0;
    digitalWrite(pinRelay, HIGH);
  }

  //turn relay off after delay
  if (relay == 0 && delayOffRelay == 0 && digitalRead(pinRelay) == HIGH) {
    timerRelay = millis();
    delayOffRelay = 1;
  }
  if (delayOffRelay == 1 && (millis() - timerRelay) > delayOffTimeRelay) {
    delayOffRelay = 0;
    digitalWrite(pinRelay, LOW);
  }


  delay(1);



  Serial.print("relay = ");
  Serial.print(relay);
  Serial.print("\t");
  Serial.print("timerRelay = ");
  Serial.print(timerRelay);
  Serial.print("\t");
  Serial.print("delayOnRelay = ");
  Serial.print(delayOnRelay);
  Serial.print("\t");
  Serial.print("delayOffRelay = ");
  Serial.print(delayOffRelay);
  Serial.print("\t");
  Serial.print("pinRelay = ");
  Serial.println(digitalRead(pinRelay));



}

is there a more elegant solution?

That depends on your definition of elegant

Personally I would go for readable and maintainable over clever but obtuse

const byte inputPin = A1;
const byte relayPin = 3;
unsigned long currentTime;
unsigned long buttonPressedTime;
unsigned long relayOnTime;
unsigned long waitPeriod = 1000;
unsigned long onPeriod = 2000;

enum
{
  WAIT_FOR_INPUT,
  WAIT_BEFORE_ON,
  WAIT_BEFORE_OFF
} relayState;


void setup()
{
  pinMode(inputPin, INPUT_PULLUP);
  pinMode(relayPin, OUTPUT);
  digitalWrite(relayPin, HIGH);
  relayState = WAIT_FOR_INPUT;
}

void loop()
{
  currentTime = millis();
  switch (relayState)
  {
    case WAIT_FOR_INPUT:
      if (digitalRead(inputPin) == LOW)
      {
        buttonPressedTime = currentTime;
        relayState = WAIT_BEFORE_ON;
      }
      break;
    case WAIT_BEFORE_ON:
      if (currentTime - buttonPressedTime >= waitPeriod)
      {
        relayOnTime = currentTime;
        digitalWrite(relayPin, LOW);
        relayState = WAIT_BEFORE_OFF;
      }
      break;
    case WAIT_BEFORE_OFF:
      if (currentTime - relayOnTime >= onPeriod)
      {
        digitalWrite(relayPin, HIGH);
        relayState = WAIT_FOR_INPUT;
        break;
      }
  }
}

Note the use of switch/case to implement a state machine, the enum to give sensible names to the relay states and the use of a variable to hold the current time so that millis() is only called once in the code. It could be improved by adding debouncing code if millisecond accuracy is required

Thanks, Bob.
I've been a hardware designer for 45 years, software is relatively new to me.