【C言語】
switch-caseの範囲指定
case ここから…そこまで

switch-caseの 範囲指定

■gcc拡張を使用したswitch-caseの範囲指定

case from ... to:
を使って範囲を指定できます。

参考:gcc(Gnu C Compiler)の拡張文法


■西暦を和暦(元号)に変換

static void 西暦和暦変換(char *西暦書式,char *和暦書式){
    uint32_t    year;
    uint32_t    mon;
    uint32_t    day;
    int ret = sscanf(西暦書式,"%04d/%02d/%02d",
        &year,
        &mon,
        &day
    );
    if(ret != 3)
        assert(0);
    /*
    元号期間
    明治    1868/09/08 〜 1912/07/29
    大正    1912/07/30 〜 1926/12/24
    昭和    1926/12/25 〜 1989/01/07
    平成    1989/01/08 〜 2019/04/30
    令和    2019/05/01 〜
    */
    
    //  このマクロは FORM+"/%d年/%02d/%02d"で文字列連結します。
    #define G(FORM,START) \
      sprintf(和暦書式,FORM "/%d年/%02d/%02d",year-(START)+1,mon,day)
    uint32_t ymd = year*10000 + mon*100 +day;
    switch(ymd){
    default:                        G("明治以前",1); break;
    case    18680908 ... 19120729:  G("明治",1868);  break; 
    case    19120730 ... 19261224:  G("大正",1912);  break;        
    case    19261225 ... 19890107:  G("昭和",1926);  break; 
    case    19890108 ... 20190430:  G("平成",1989);  break; 
    case    20190501 ... INT_MAX:   G("令和",2019);  break;   
    }
}

例: 2023/06/03 ->令和/5年/06/03

GNUの拡張機能を使用すると
case 18680908 … 19120729:
のように、case 文の範囲指定が出来ます。

gccとclangで使用できますが、
Visual Studio ではコンパイルエラーになります。


■和暦(元号)を西暦に変換

static void 和暦西暦変換(char *和暦書式,char *西暦書式){
    char        元号[32];
    uint32_t    year;
    uint32_t    mon;
    uint32_t    day;
    int ret = sscanf(和暦書式,"%[^/]/%d年/%d/%d",
        元号,
        &year,
        &mon,
        &day
    );
    if(ret != 4){
        fprintf(stderr,"ret=%d,%s:%d:%d:%d\n",ret,元号,year,mon,day);
        fflush(NULL);
        assert(0);
    }
    #define CMP(X,Y)  (strcmp(X,Y)==0)
    if(     CMP(元号,"令和")) {  year += 2019-1; }
    else if(CMP(元号,"平成")) {  year += 1989-1; }
    else if(CMP(元号,"昭和")) {  year += 1926-1; }
    else if(CMP(元号,"大正")) {  year += 1912-1; }
    else if(CMP(元号,"明治")) {  year += 1868-1; }
    else                     {  year += 0;      }
    sprintf(西暦書式,"%04d/%02d/%02d",year,mon,day);
}

例: 令和/5年/06/03->2023/06/03

■まとめ(コンパイル可能)

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <assert.h>
//例: 2023/06/03 ->令和/5年/06/03
static void 西暦和暦変換(char *西暦書式,char *和暦書式){
    uint32_t    year;
    uint32_t    mon;
    uint32_t    day;
    int ret = sscanf(西暦書式,"%04d/%02d/%02d",
        &year,
        &mon,
        &day
    );
    if(ret != 3)
        assert(0);
    /*
    元号期間
    明治    1868/09/08 〜 1912/07/29
    大正    1912/07/30 〜 1926/12/24
    昭和    1926/12/25 〜 1989/01/07
    平成    1989/01/08 〜 2019/04/30
    令和    2019/05/01 〜
    */
    
    //  このマクロは FORM+"/%d年/%02d/%02d"で文字列連結します。
    #define G(FORM,START) \
      sprintf(和暦書式,FORM "/%d年/%02d/%02d",year-(START)+1,mon,day)
    uint32_t ymd = year*10000 + mon*100 +day;
    switch(ymd){
    default:                        G("明治以前",1); break;
    case    18680908 ... 19120729:  G("明治",1868);  break; 
    case    19120730 ... 19261224:  G("大正",1912);  break;        
    case    19261225 ... 19890107:  G("昭和",1926);  break; 
    case    19890108 ... 20190430:  G("平成",1989);  break; 
    case    20190501 ... INT_MAX:   G("令和",2019);  break;   
    }
}
//例: 令和/5年/06/03->2023/06/03
static void 和暦西暦変換(char *和暦書式,char *西暦書式){
    char        元号[32];
    uint32_t    year;
    uint32_t    mon;
    uint32_t    day;
    int ret = sscanf(和暦書式,"%[^/]/%d年/%d/%d",
        元号,
        &year,
        &mon,
        &day
    );
    if(ret != 4){
        fprintf(stderr,"ret=%d,%s:%d:%d:%d\n",ret,元号,year,mon,day);
        fflush(NULL);
        assert(0);
    }
    #define CMP(X,Y)  (strcmp(X,Y)==0)
    if(     CMP(元号,"令和")) {  year += 2019-1; }
    else if(CMP(元号,"平成")) {  year += 1989-1; }
    else if(CMP(元号,"昭和")) {  year += 1926-1; }
    else if(CMP(元号,"大正")) {  year += 1912-1; }
    else if(CMP(元号,"明治")) {  year += 1868-1; }
    else                     {  year += 0;      }
    sprintf(西暦書式,"%04d/%02d/%02d",year,mon,day);
}
int main(void){
  uint32_t mdays[] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
  for(uint32_t  ymd = 18670908 ; ymd < 21001231;ymd++){
    uint32_t  year = (ymd/10000) ;
    uint32_t  mon  = (ymd/100)%100;
    uint32_t  day  = (ymd)%100;
    if(mon < 1 || 12 < mon) 
      continue ;
    if(day < 1 || mdays[mon] < day)//閏年無視 
      continue ;
    
    //西暦1⇒和暦2⇒西暦3の順番で変換する
    char    西暦1[256];
    char    和暦2[256];
    char    西暦3[256];
    sprintf(西暦1,"%04d/%02d/%02d",year,mon,day);
    printf("%s->",西暦1);
    西暦和暦変換(西暦1,和暦2);
    printf("%s->",和暦2); 
    和暦西暦変換(和暦2,西暦3);
    printf("%s\n",西暦3);

    //元に戻らなければ変換失敗
    if(strcmp(西暦1,西暦3) != 0){  
      fflush(NULL);
      assert(0);
    }
  }
}